Fossil SCM
merge trunk
Commit
a68dffbff3c256ffd4a6f241ecd2c013f9fbb84b
Parent
bab2f28b601c0ab…
59 files changed
+1
-1
+1
+3
-3
+3
-3
+6
-10
+6
-2
+48
-46
+33
-2
+265
-87
+265
-87
+9
-3
+2
-2
+2
-3
+29
-27
+2
-4
+4
-10
+50
-15
+1
-3
+1
-1
+1
-1
+1
-1
+2
-2
+17
-27
+21
-1
+2
+21
-3
+72
-9
+2
-2
+15
+3
+50
+2
+47
-1
+6
+12
-7
+1
+720
-305
+4
-4
+97
-6
+18
-4
+22
-35
+73
-16
+2
+528
+10
-5
+7
-3
+1
-1
+23
+16
-4
+24
+24
+20
+3
+398
-204
+3
-4
+40
+2
+1
+7
~
src/add.c
~
src/cgi.c
~
src/checkin.c
~
src/checkin.c
~
src/cson_amalgamation.c
~
src/cson_amalgamation.h
~
src/db.c
~
src/descendants.c
~
src/diff.c
~
src/diff.c
~
src/diffcmd.c
~
src/doc.c
~
src/encode.c
~
src/file.c
~
src/http_socket.c
~
src/http_ssl.c
~
src/info.c
~
src/json.c
~
src/json_diff.c
~
src/json_timeline.c
~
src/json_wiki.c
~
src/login.c
~
src/main.c
~
src/main.mk
~
src/makemake.tcl
~
src/manifest.c
~
src/merge.c
~
src/merge3.c
~
src/printf.c
~
src/rebuild.c
~
src/regexp.c
~
src/schema.c
~
src/setup.c
~
src/shell.c
~
src/skins.c
~
src/sqlcmd.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/stat.c
~
src/style.c
~
src/timeline.c
~
src/tkt.c
~
src/tktsetup.c
~
src/unicode.c
~
src/update.c
~
src/user.c
~
src/wiki.c
~
test/diff-test-1.wiki
~
win/Makefile.dmc
~
win/Makefile.mingw
~
win/Makefile.mingw.mistachkin
~
win/Makefile.msc
~
win/fossil.rc
~
win/include/dirent.h
~
www/checkin_names.wiki
~
www/fiveminutes.wiki
~
www/index.wiki
~
www/mkindex.tcl
~
www/permutedindex.wiki
+1
-1
| --- src/add.c | ||
| +++ src/add.c | ||
| @@ -140,11 +140,11 @@ | ||
| 140 | 140 | const char *zPath, /* Tree-name of file to add. */ |
| 141 | 141 | int vid, /* Add to this VFILE */ |
| 142 | 142 | int caseSensitive /* True if filenames are case sensitive */ |
| 143 | 143 | ){ |
| 144 | 144 | const char *zCollate = caseSensitive ? "binary" : "nocase"; |
| 145 | - if( !file_is_simple_pathname(zPath) ){ | |
| 145 | + if( !file_is_simple_pathname(zPath, 1) ){ | |
| 146 | 146 | fossil_warning("filename contains illegal characters: %s", zPath); |
| 147 | 147 | return 0; |
| 148 | 148 | } |
| 149 | 149 | if( db_exists("SELECT 1 FROM vfile" |
| 150 | 150 | " WHERE pathname=%Q COLLATE %s", zPath, zCollate) ){ |
| 151 | 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) ){ |
| 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 @@ | ||
| 21 | 21 | ** formatting function and its cousins, and routines to encode and |
| 22 | 22 | ** decode strings in HTML or HTTP. |
| 23 | 23 | */ |
| 24 | 24 | #include "config.h" |
| 25 | 25 | #ifdef _WIN32 |
| 26 | +# include <winsock2.h> | |
| 26 | 27 | # include <ws2tcpip.h> |
| 27 | 28 | #else |
| 28 | 29 | # include <sys/socket.h> |
| 29 | 30 | # include <netinet/in.h> |
| 30 | 31 | # include <arpa/inet.h> |
| 31 | 32 |
| --- 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 @@ | ||
| 316 | 316 | ** unless overridden by the --abs-paths or --rel-paths options. |
| 317 | 317 | ** |
| 318 | 318 | ** Options: |
| 319 | 319 | ** --abs-paths Display absolute pathnames. |
| 320 | 320 | ** --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 | |
| 322 | 322 | ** --rel-paths Display pathnames relative to the current working |
| 323 | 323 | ** directory. |
| 324 | 324 | ** |
| 325 | 325 | ** See also: changes, clean, status |
| 326 | 326 | */ |
| @@ -1182,11 +1182,11 @@ | ||
| 1182 | 1182 | } |
| 1183 | 1183 | |
| 1184 | 1184 | /* So that older versions of Fossil (that do not understand delta- |
| 1185 | 1185 | ** manifest) can continue to use this repository, do not create a new |
| 1186 | 1186 | ** 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 | |
| 1188 | 1188 | ** by the --delta option. |
| 1189 | 1189 | */ |
| 1190 | 1190 | if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){ |
| 1191 | 1191 | forceBaseline = 1; |
| 1192 | 1192 | } |
| @@ -1565,11 +1565,11 @@ | ||
| 1565 | 1565 | exit(1); |
| 1566 | 1566 | } |
| 1567 | 1567 | db_end_transaction(0); |
| 1568 | 1568 | |
| 1569 | 1569 | if( !g.markPrivate ){ |
| 1570 | - autosync(SYNC_PUSH); | |
| 1570 | + autosync(SYNC_PUSH|SYNC_PULL); | |
| 1571 | 1571 | } |
| 1572 | 1572 | if( count_nonbranch_children(vid)>1 ){ |
| 1573 | 1573 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 1574 | 1574 | } |
| 1575 | 1575 | } |
| 1576 | 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 |
| 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 @@ | ||
| 316 | 316 | ** unless overridden by the --abs-paths or --rel-paths options. |
| 317 | 317 | ** |
| 318 | 318 | ** Options: |
| 319 | 319 | ** --abs-paths Display absolute pathnames. |
| 320 | 320 | ** --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 | |
| 322 | 322 | ** --rel-paths Display pathnames relative to the current working |
| 323 | 323 | ** directory. |
| 324 | 324 | ** |
| 325 | 325 | ** See also: changes, clean, status |
| 326 | 326 | */ |
| @@ -1182,11 +1182,11 @@ | ||
| 1182 | 1182 | } |
| 1183 | 1183 | |
| 1184 | 1184 | /* So that older versions of Fossil (that do not understand delta- |
| 1185 | 1185 | ** manifest) can continue to use this repository, do not create a new |
| 1186 | 1186 | ** 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 | |
| 1188 | 1188 | ** by the --delta option. |
| 1189 | 1189 | */ |
| 1190 | 1190 | if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){ |
| 1191 | 1191 | forceBaseline = 1; |
| 1192 | 1192 | } |
| @@ -1565,11 +1565,11 @@ | ||
| 1565 | 1565 | exit(1); |
| 1566 | 1566 | } |
| 1567 | 1567 | db_end_transaction(0); |
| 1568 | 1568 | |
| 1569 | 1569 | if( !g.markPrivate ){ |
| 1570 | - autosync(SYNC_PUSH); | |
| 1570 | + autosync(SYNC_PUSH|SYNC_PULL); | |
| 1571 | 1571 | } |
| 1572 | 1572 | if( count_nonbranch_children(vid)>1 ){ |
| 1573 | 1573 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 1574 | 1574 | } |
| 1575 | 1575 | } |
| 1576 | 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 |
| 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 |
+6
-10
| --- src/cson_amalgamation.c | ||
| +++ src/cson_amalgamation.c | ||
| @@ -1422,11 +1422,10 @@ | ||
| 1422 | 1422 | extern "C" { |
| 1423 | 1423 | #endif |
| 1424 | 1424 | |
| 1425 | 1425 | |
| 1426 | 1426 | |
| 1427 | - | |
| 1428 | 1427 | /** |
| 1429 | 1428 | This type holds the "vtbl" for type-specific operations when |
| 1430 | 1429 | working with cson_value objects. |
| 1431 | 1430 | |
| 1432 | 1431 | All cson_values of a given logical type share a pointer to a single |
| @@ -1524,13 +1523,11 @@ | ||
| 1524 | 1523 | */ |
| 1525 | 1524 | #define cson_value_empty_m { &cson_value_api_empty/*api*/, NULL/*value*/, 0/*refcount*/ } |
| 1526 | 1525 | /** |
| 1527 | 1526 | Empty-initialized cson_value object. |
| 1528 | 1527 | */ |
| 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; | |
| 1532 | 1529 | const cson_parse_opt cson_parse_opt_empty = cson_parse_opt_empty_m; |
| 1533 | 1530 | const cson_output_opt cson_output_opt_empty = cson_output_opt_empty_m; |
| 1534 | 1531 | const cson_object_iterator cson_object_iterator_empty = cson_object_iterator_empty_m; |
| 1535 | 1532 | const cson_buffer cson_buffer_empty = cson_buffer_empty_m; |
| 1536 | 1533 | const cson_parse_info cson_parse_info_empty = cson_parse_info_empty_m; |
| @@ -1661,11 +1658,11 @@ | ||
| 1661 | 1658 | { |
| 1662 | 1659 | if((m >= (void const *)&CSON_EMPTY_HOLDER) |
| 1663 | 1660 | && ( m < (void const *)(&CSON_EMPTY_HOLDER+1))) |
| 1664 | 1661 | return 1; |
| 1665 | 1662 | else return |
| 1666 | - ((m > (void const *)&CSON_SPECIAL_VALUES[0]) | |
| 1663 | + ((m >= (void const *)&CSON_SPECIAL_VALUES[0]) | |
| 1667 | 1664 | && ( m < (void const *)&CSON_SPECIAL_VALUES[CSON_INTERNAL_VALUES_LENGTH]) ) |
| 1668 | 1665 | ? 1 |
| 1669 | 1666 | : 0; |
| 1670 | 1667 | } |
| 1671 | 1668 | |
| @@ -1711,13 +1708,13 @@ | ||
| 1711 | 1708 | malloc/free funcs because fossil's lack of header files |
| 1712 | 1709 | means we would have to #include "main.c" here to |
| 1713 | 1710 | get the declarations. |
| 1714 | 1711 | */ |
| 1715 | 1712 | #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); | |
| 1719 | 1716 | # define CSON_MALLOC_IMPL fossil_malloc |
| 1720 | 1717 | # define CSON_FREE_IMPL fossil_free |
| 1721 | 1718 | # define CSON_REALLOC_IMPL fossil_realloc |
| 1722 | 1719 | #endif |
| 1723 | 1720 | |
| @@ -4380,11 +4377,11 @@ | ||
| 4380 | 4377 | arg MUST be a (cson_buffer*). This function appends n bytes at |
| 4381 | 4378 | position arg->used, expanding the buffer as necessary. |
| 4382 | 4379 | */ |
| 4383 | 4380 | static int cson_data_dest_cson_buffer( void * arg, void const * data_, unsigned int n ) |
| 4384 | 4381 | { |
| 4385 | - if( ! arg || (n<0) ) return cson_rc.ArgError; | |
| 4382 | + if( !arg ) return cson_rc.ArgError; | |
| 4386 | 4383 | else if( ! n ) return 0; |
| 4387 | 4384 | else |
| 4388 | 4385 | { |
| 4389 | 4386 | cson_buffer * sb = (cson_buffer*)arg; |
| 4390 | 4387 | char const * data = (char const *)data_; |
| @@ -4500,11 +4497,10 @@ | ||
| 4500 | 4497 | cson_value * cv = NULL; |
| 4501 | 4498 | cson_object const * curObj = obj; |
| 4502 | 4499 | enum { BufSize = 128 }; |
| 4503 | 4500 | char buf[BufSize]; |
| 4504 | 4501 | memset( buf, 0, BufSize ); |
| 4505 | - rc = cson_rc.RangeError; | |
| 4506 | 4502 | |
| 4507 | 4503 | while( cson_next_token( &beg, sep, &end ) ) |
| 4508 | 4504 | { |
| 4509 | 4505 | if( beg == end ) break; |
| 4510 | 4506 | else |
| 4511 | 4507 |
| --- 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 |
+6
-2
| --- src/cson_amalgamation.h | ||
| +++ src/cson_amalgamation.h | ||
| @@ -1236,11 +1236,14 @@ | ||
| 1236 | 1236 | eventually either free the value using cson_value_free() or |
| 1237 | 1237 | inserting it into a container (array or object), which transfers |
| 1238 | 1238 | ownership to the container. See the cson_value class documentation |
| 1239 | 1239 | for more details. |
| 1240 | 1240 | |
| 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. | |
| 1242 | 1245 | */ |
| 1243 | 1246 | cson_value * cson_value_new_bool( char v ); |
| 1244 | 1247 | |
| 1245 | 1248 | |
| 1246 | 1249 | /** |
| @@ -1927,11 +1930,12 @@ | ||
| 1927 | 1930 | modified. |
| 1928 | 1931 | |
| 1929 | 1932 | buf->mem is owned by buf and must eventually be freed by passing an |
| 1930 | 1933 | n value of 0 to this function. |
| 1931 | 1934 | |
| 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. | |
| 1933 | 1937 | */ |
| 1934 | 1938 | int cson_buffer_reserve( cson_buffer * buf, cson_size_t n ); |
| 1935 | 1939 | |
| 1936 | 1940 | /** |
| 1937 | 1941 | Fills all bytes of the given buffer with the given character. |
| 1938 | 1942 |
| --- 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 |
M
src/db.c
+48
-46
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -22,11 +22,11 @@ | ||
| 22 | 22 | ** |
| 23 | 23 | ** (1) The "user" database in ~/.fossil |
| 24 | 24 | ** |
| 25 | 25 | ** (2) The "repository" database |
| 26 | 26 | ** |
| 27 | -** (3) A local checkout database named "_FOSSIL_" or ".fos" | |
| 27 | +** (3) A local checkout database named "_FOSSIL_" or ".fslckout" | |
| 28 | 28 | ** and located at the root of the local copy of the source tree. |
| 29 | 29 | ** |
| 30 | 30 | */ |
| 31 | 31 | #include "config.h" |
| 32 | 32 | #if ! defined(_WIN32) |
| @@ -89,11 +89,11 @@ | ||
| 89 | 89 | cgi_reply(); |
| 90 | 90 | } |
| 91 | 91 | else if( g.cgiOutput ){ |
| 92 | 92 | g.cgiOutput = 0; |
| 93 | 93 | 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); | |
| 95 | 95 | cgi_reply(); |
| 96 | 96 | }else{ |
| 97 | 97 | fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg); |
| 98 | 98 | } |
| 99 | 99 | free(z); |
| @@ -484,20 +484,30 @@ | ||
| 484 | 484 | /* |
| 485 | 485 | ** Execute multiple SQL statements. |
| 486 | 486 | */ |
| 487 | 487 | int db_multi_exec(const char *zSql, ...){ |
| 488 | 488 | Blob sql; |
| 489 | - int rc; | |
| 489 | + int rc = SQLITE_OK; | |
| 490 | 490 | va_list ap; |
| 491 | - char *zErr = 0; | |
| 491 | + const char *z, *zEnd; | |
| 492 | + sqlite3_stmt *pStmt; | |
| 492 | 493 | blob_init(&sql, 0, 0); |
| 493 | 494 | va_start(ap, zSql); |
| 494 | 495 | blob_vappendf(&sql, zSql, ap); |
| 495 | 496 | 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; | |
| 499 | 509 | } |
| 500 | 510 | blob_reset(&sql); |
| 501 | 511 | return rc; |
| 502 | 512 | } |
| 503 | 513 | |
| @@ -642,15 +652,11 @@ | ||
| 642 | 652 | sqlite3 *db; |
| 643 | 653 | int rc; |
| 644 | 654 | const char *zSql; |
| 645 | 655 | va_list ap; |
| 646 | 656 | |
| 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); | |
| 652 | 658 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 653 | 659 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 654 | 660 | if( rc!=SQLITE_OK ){ |
| 655 | 661 | db_err(sqlite3_errmsg(db)); |
| 656 | 662 | } |
| @@ -697,29 +703,43 @@ | ||
| 697 | 703 | |
| 698 | 704 | /* |
| 699 | 705 | ** Open a database file. Return a pointer to the new database |
| 700 | 706 | ** connection. An error results in process abort. |
| 701 | 707 | */ |
| 702 | -static sqlite3 *openDatabase(const char *zDbName){ | |
| 708 | +LOCAL sqlite3 *db_open(const char *zDbName){ | |
| 703 | 709 | int rc; |
| 704 | 710 | const char *zVfs; |
| 705 | 711 | sqlite3 *db; |
| 706 | 712 | |
| 713 | + if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); | |
| 707 | 714 | zVfs = fossil_getenv("FOSSIL_VFS"); |
| 708 | 715 | rc = sqlite3_open_v2( |
| 709 | 716 | zDbName, &db, |
| 710 | 717 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 711 | 718 | zVfs |
| 712 | 719 | ); |
| 713 | 720 | if( rc!=SQLITE_OK ){ |
| 714 | - db_err(sqlite3_errmsg(db)); | |
| 721 | + db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); | |
| 715 | 722 | } |
| 716 | 723 | sqlite3_busy_timeout(db, 5000); |
| 717 | 724 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 718 | 725 | sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); |
| 719 | 726 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, |
| 720 | 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); | |
| 721 | 741 | return db; |
| 722 | 742 | } |
| 723 | 743 | |
| 724 | 744 | |
| 725 | 745 | /* |
| @@ -747,13 +767,12 @@ | ||
| 747 | 767 | const char *zLabel, |
| 748 | 768 | int *pWasAttached |
| 749 | 769 | ){ |
| 750 | 770 | if( !g.db ){ |
| 751 | 771 | assert( g.zMainDbType==0 ); |
| 752 | - g.db = openDatabase(zDbName); | |
| 772 | + g.db = db_open(zDbName); | |
| 753 | 773 | g.zMainDbType = zLabel; |
| 754 | - db_connection_init(); | |
| 755 | 774 | if ( pWasAttached ) *pWasAttached = 0; |
| 756 | 775 | }else{ |
| 757 | 776 | assert( g.zMainDbType!=0 ); |
| 758 | 777 | db_attach(zDbName, zLabel); |
| 759 | 778 | if ( pWasAttached ) *pWasAttached = 1; |
| @@ -820,11 +839,11 @@ | ||
| 820 | 839 | db_open_or_attach(zDbName, "configdb", &g.useAttach); |
| 821 | 840 | g.dbConfig = 0; |
| 822 | 841 | g.zConfigDbType = 0; |
| 823 | 842 | }else{ |
| 824 | 843 | g.useAttach = 0; |
| 825 | - g.dbConfig = openDatabase(zDbName); | |
| 844 | + g.dbConfig = db_open(zDbName); | |
| 826 | 845 | g.zConfigDbType = "configdb"; |
| 827 | 846 | } |
| 828 | 847 | g.configOpen = 1; |
| 829 | 848 | free(zDbName); |
| 830 | 849 | } |
| @@ -1221,10 +1240,13 @@ | ||
| 1221 | 1240 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ |
| 1222 | 1241 | const char *zUser = zDefaultUser; |
| 1223 | 1242 | if( zUser==0 ){ |
| 1224 | 1243 | zUser = db_get("default-user", 0); |
| 1225 | 1244 | } |
| 1245 | + if( zUser==0 ){ | |
| 1246 | + zUser = fossil_getenv("FOSSIL_USER"); | |
| 1247 | + } | |
| 1226 | 1248 | if( zUser==0 ){ |
| 1227 | 1249 | #if defined(_WIN32) |
| 1228 | 1250 | zUser = fossil_getenv("USERNAME"); |
| 1229 | 1251 | #else |
| 1230 | 1252 | zUser = fossil_getenv("USER"); |
| @@ -1441,11 +1463,11 @@ | ||
| 1441 | 1463 | ** SQL functions for debugging. |
| 1442 | 1464 | ** |
| 1443 | 1465 | ** The print() function writes its arguments on stdout, but only |
| 1444 | 1466 | ** if the -sqlprint command-line option is turned on. |
| 1445 | 1467 | */ |
| 1446 | -static void db_sql_print( | |
| 1468 | +LOCAL void db_sql_print( | |
| 1447 | 1469 | sqlite3_context *context, |
| 1448 | 1470 | int argc, |
| 1449 | 1471 | sqlite3_value **argv |
| 1450 | 1472 | ){ |
| 1451 | 1473 | int i; |
| @@ -1454,22 +1476,20 @@ | ||
| 1454 | 1476 | char c = i==argc-1 ? '\n' : ' '; |
| 1455 | 1477 | fossil_print("%s%c", sqlite3_value_text(argv[i]), c); |
| 1456 | 1478 | } |
| 1457 | 1479 | } |
| 1458 | 1480 | } |
| 1459 | -static void db_sql_trace(void *notUsed, const char *zSql){ | |
| 1481 | +LOCAL void db_sql_trace(void *notUsed, const char *zSql){ | |
| 1460 | 1482 | 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]==';') ? "" : ";"); | |
| 1464 | 1484 | } |
| 1465 | 1485 | |
| 1466 | 1486 | /* |
| 1467 | 1487 | ** Implement the user() SQL function. user() takes no arguments and |
| 1468 | 1488 | ** returns the user ID of the current user. |
| 1469 | 1489 | */ |
| 1470 | -static void db_sql_user( | |
| 1490 | +LOCAL void db_sql_user( | |
| 1471 | 1491 | sqlite3_context *context, |
| 1472 | 1492 | int argc, |
| 1473 | 1493 | sqlite3_value **argv |
| 1474 | 1494 | ){ |
| 1475 | 1495 | if( g.zLogin!=0 ){ |
| @@ -1481,11 +1501,11 @@ | ||
| 1481 | 1501 | ** Implement the cgi() SQL function. cgi() takes an argument which is |
| 1482 | 1502 | ** a name of CGI query parameter. The value of that parameter is returned, |
| 1483 | 1503 | ** if available. Optional second argument will be returned if the first |
| 1484 | 1504 | ** doesn't exist as a CGI parameter. |
| 1485 | 1505 | */ |
| 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){ | |
| 1487 | 1507 | const char* zP; |
| 1488 | 1508 | if( argc!=1 && argc!=2 ) return; |
| 1489 | 1509 | zP = P((const char*)sqlite3_value_text(argv[0])); |
| 1490 | 1510 | if( zP ){ |
| 1491 | 1511 | sqlite3_result_text(context, zP, -1, SQLITE_STATIC); |
| @@ -1512,11 +1532,11 @@ | ||
| 1512 | 1532 | ** (meaning that id was named on the command-line). |
| 1513 | 1533 | ** |
| 1514 | 1534 | ** In the second form (3 arguments) return argument X if true and Y |
| 1515 | 1535 | ** if false. Except if Y is NULL then always return X. |
| 1516 | 1536 | */ |
| 1517 | -static void file_is_selected( | |
| 1537 | +LOCAL void file_is_selected( | |
| 1518 | 1538 | sqlite3_context *context, |
| 1519 | 1539 | int argc, |
| 1520 | 1540 | sqlite3_value **argv |
| 1521 | 1541 | ){ |
| 1522 | 1542 | int rc = 0; |
| @@ -1600,31 +1620,10 @@ | ||
| 1600 | 1620 | zOut = mprintf("%s", zKey); |
| 1601 | 1621 | } |
| 1602 | 1622 | return zOut; |
| 1603 | 1623 | } |
| 1604 | 1624 | |
| 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 | 1625 | /* |
| 1627 | 1626 | ** Return true if the string zVal represents "true" (or "false"). |
| 1628 | 1627 | */ |
| 1629 | 1628 | int is_truth(const char *zVal){ |
| 1630 | 1629 | static const char *const azOn[] = { "on", "yes", "true", "1" }; |
| @@ -2115,10 +2114,13 @@ | ||
| 2115 | 2114 | ** file named .fossil-settings/PROPERTY in the checked out files, if that |
| 2116 | 2115 | ** file exists. |
| 2117 | 2116 | ** |
| 2118 | 2117 | ** The "unset" command clears a property setting. |
| 2119 | 2118 | ** |
| 2119 | +** | |
| 2120 | +** access-log If enabled, record successful and failed login attempts | |
| 2121 | +** in the "accesslog" table. Default: off | |
| 2120 | 2122 | ** |
| 2121 | 2123 | ** allow-symlinks If enabled, don't follow symlinks, and instead treat |
| 2122 | 2124 | ** (versionable) them as symlinks on Unix. Has no effect on Windows |
| 2123 | 2125 | ** (existing links in repository created on Unix become |
| 2124 | 2126 | ** plain-text files with link destination path inside). |
| 2125 | 2127 |
| --- 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 |
+33
-2
| --- src/descendants.c | ||
| +++ src/descendants.c | ||
| @@ -348,10 +348,11 @@ | ||
| 348 | 348 | ** repository database to be recomputed. |
| 349 | 349 | ** |
| 350 | 350 | ** Options: |
| 351 | 351 | ** --all show ALL leaves |
| 352 | 352 | ** --closed show only closed leaves |
| 353 | +** --bybranch order output by branch name | |
| 353 | 354 | ** --recompute recompute the "leaf" table in the repository DB |
| 354 | 355 | ** |
| 355 | 356 | ** See also: descendants, finfo, info, branch |
| 356 | 357 | */ |
| 357 | 358 | void leaves_cmd(void){ |
| @@ -358,10 +359,14 @@ | ||
| 358 | 359 | Stmt q; |
| 359 | 360 | Blob sql; |
| 360 | 361 | int showAll = find_option("all", 0, 0)!=0; |
| 361 | 362 | int showClosed = find_option("closed", 0, 0)!=0; |
| 362 | 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]; | |
| 363 | 368 | |
| 364 | 369 | db_find_and_open_repository(0,0); |
| 365 | 370 | if( recomputeFlag ) leaf_rebuild(); |
| 366 | 371 | blob_zero(&sql); |
| 367 | 372 | blob_append(&sql, timeline_query_for_tty(), -1); |
| @@ -369,13 +374,39 @@ | ||
| 369 | 374 | if( showClosed ){ |
| 370 | 375 | blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid")); |
| 371 | 376 | }else if( !showAll ){ |
| 372 | 377 | blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid")); |
| 373 | 378 | } |
| 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 | + } | |
| 375 | 386 | 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); | |
| 377 | 408 | db_finalize(&q); |
| 378 | 409 | } |
| 379 | 410 | |
| 380 | 411 | /* |
| 381 | 412 | ** WEBPAGE: leaves |
| 382 | 413 |
| --- 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 @@ | ||
| 23 | 23 | #include <assert.h> |
| 24 | 24 | |
| 25 | 25 | |
| 26 | 26 | #if INTERFACE |
| 27 | 27 | /* |
| 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. | |
| 29 | 30 | */ |
| 30 | 31 | #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */ |
| 31 | 32 | #define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */ |
| 32 | 33 | #define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */ |
| 33 | 34 | #define DIFF_SIDEBYSIDE ((u64)0x02000000) /* Generate a side-by-side diff */ |
| @@ -37,10 +38,11 @@ | ||
| 37 | 38 | #define DIFF_HTML ((u64)0x10000000) /* Render for HTML */ |
| 38 | 39 | #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */ |
| 39 | 40 | #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */ |
| 40 | 41 | #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */ |
| 41 | 42 | #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */ |
| 43 | +#define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */ | |
| 42 | 44 | |
| 43 | 45 | /* |
| 44 | 46 | ** These error messages are shared in multiple locations. They are defined |
| 45 | 47 | ** here for consistency. |
| 46 | 48 | */ |
| @@ -52,11 +54,11 @@ | ||
| 52 | 54 | |
| 53 | 55 | #define looks_like_binary(blob) ((looks_like_utf8((blob))&3) == 0) |
| 54 | 56 | #endif /* INTERFACE */ |
| 55 | 57 | |
| 56 | 58 | /* |
| 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) | |
| 58 | 60 | */ |
| 59 | 61 | #define LENGTH_MASK_SZ 13 |
| 60 | 62 | #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1) |
| 61 | 63 | |
| 62 | 64 | /* |
| @@ -116,10 +118,13 @@ | ||
| 116 | 118 | ** more. If trailing whitespace is ignored, the "patch" command gets |
| 117 | 119 | ** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b] |
| 118 | 120 | ** |
| 119 | 121 | ** Return 0 if the file is binary or contains a line that is |
| 120 | 122 | ** too long. |
| 123 | +** | |
| 124 | +** Profiling show that in most cases this routine consumes the bulk of | |
| 125 | +** the CPU time on a diff. | |
| 121 | 126 | */ |
| 122 | 127 | static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){ |
| 123 | 128 | int nLine, i, j, k, x; |
| 124 | 129 | unsigned int h, h2; |
| 125 | 130 | DLine *a; |
| @@ -445,34 +450,50 @@ | ||
| 445 | 450 | ** Return true if two DLine elements are identical. |
| 446 | 451 | */ |
| 447 | 452 | static int same_dline(DLine *pA, DLine *pB){ |
| 448 | 453 | return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0; |
| 449 | 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 | +} | |
| 450 | 473 | |
| 451 | 474 | /* |
| 452 | 475 | ** Append a single line of context-diff output to pOut. |
| 453 | 476 | */ |
| 454 | 477 | static void appendDiffLine( |
| 455 | 478 | Blob *pOut, /* Where to write the line of output */ |
| 456 | 479 | char cPrefix, /* One of " ", "+", or "-" */ |
| 457 | 480 | 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 */ | |
| 459 | 483 | ){ |
| 460 | - int i; | |
| 461 | 484 | blob_append(pOut, &cPrefix, 1); |
| 462 | 485 | if( html ){ |
| 463 | 486 | char *zHtml; |
| 464 | - if( cPrefix=='+' ){ | |
| 487 | + if( pRe && re_dline_match(pRe, pLine, 1)==0 ){ | |
| 488 | + cPrefix = ' '; | |
| 489 | + }else if( cPrefix=='+' ){ | |
| 465 | 490 | blob_append(pOut, "<span class=\"diffadd\">", -1); |
| 466 | 491 | }else if( cPrefix=='-' ){ |
| 467 | 492 | blob_append(pOut, "<span class=\"diffrm\">", -1); |
| 468 | 493 | } |
| 469 | 494 | 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 | 495 | blob_append(pOut, zHtml, -1); |
| 475 | 496 | fossil_free(zHtml); |
| 476 | 497 | if( cPrefix!=' ' ){ |
| 477 | 498 | blob_append(pOut, "</span>", -1); |
| 478 | 499 | } |
| @@ -482,11 +503,11 @@ | ||
| 482 | 503 | blob_append(pOut, "\n", 1); |
| 483 | 504 | } |
| 484 | 505 | |
| 485 | 506 | /* |
| 486 | 507 | ** 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 | |
| 488 | 509 | ** to leave that number field blank. The "html" parameter means to format |
| 489 | 510 | ** the output for HTML. |
| 490 | 511 | */ |
| 491 | 512 | static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){ |
| 492 | 513 | if( html ) blob_append(pOut, "<span class=\"diffln\">", -1); |
| @@ -501,21 +522,19 @@ | ||
| 501 | 522 | blob_append(pOut, " ", 8); |
| 502 | 523 | } |
| 503 | 524 | if( html ) blob_append(pOut, "</span>", -1); |
| 504 | 525 | } |
| 505 | 526 | |
| 506 | - | |
| 507 | 527 | /* |
| 508 | 528 | ** Given a raw diff p[] in which the p->aEdit[] array has been filled |
| 509 | 529 | ** in, compute a context diff into pOut. |
| 510 | 530 | */ |
| 511 | 531 | static void contextDiff( |
| 512 | 532 | DContext *p, /* The difference */ |
| 513 | 533 | 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 */ | |
| 517 | 536 | ){ |
| 518 | 537 | DLine *A; /* Left side of the diff */ |
| 519 | 538 | DLine *B; /* Right side of the diff */ |
| 520 | 539 | int a = 0; /* Index of next line in A[] */ |
| 521 | 540 | int b = 0; /* Index of next line in B[] */ |
| @@ -526,11 +545,18 @@ | ||
| 526 | 545 | int na, nb; /* Number of lines shown from A and B */ |
| 527 | 546 | int i, j; /* Loop counters */ |
| 528 | 547 | int m; /* Number of lines to output */ |
| 529 | 548 | int skip; /* Number of lines to skip */ |
| 530 | 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 */ | |
| 531 | 554 | |
| 555 | + nContext = diff_context_lines(diffFlags); | |
| 556 | + showLn = (diffFlags & DIFF_LINENO)!=0; | |
| 557 | + html = (diffFlags & DIFF_HTML)!=0; | |
| 532 | 558 | A = p->aFrom; |
| 533 | 559 | B = p->aTo; |
| 534 | 560 | R = p->aEdit; |
| 535 | 561 | mxr = p->nEdit; |
| 536 | 562 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| @@ -537,10 +563,35 @@ | ||
| 537 | 563 | for(r=0; r<mxr; r += 3*nr){ |
| 538 | 564 | /* Figure out how many triples to show in a single block */ |
| 539 | 565 | for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){} |
| 540 | 566 | /* printf("r=%d nr=%d\n", r, nr); */ |
| 541 | 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 | + | |
| 542 | 593 | /* For the current block comprising nr triples, figure out |
| 543 | 594 | ** how many lines of A and B are to be displayed |
| 544 | 595 | */ |
| 545 | 596 | if( R[r]>nContext ){ |
| 546 | 597 | na = nb = nContext; |
| @@ -564,17 +615,18 @@ | ||
| 564 | 615 | na += R[r+i*3]; |
| 565 | 616 | nb += R[r+i*3]; |
| 566 | 617 | } |
| 567 | 618 | |
| 568 | 619 | /* 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 | |
| 570 | 621 | ** the previous block. |
| 571 | 622 | */ |
| 572 | 623 | nChunk++; |
| 573 | 624 | if( showLn ){ |
| 574 | - if( r==0 ){ | |
| 625 | + if( !showDivider ){ | |
| 575 | 626 | /* Do not show a top divider */ |
| 627 | + showDivider = 1; | |
| 576 | 628 | }else if( html ){ |
| 577 | 629 | blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.'); |
| 578 | 630 | blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk); |
| 579 | 631 | }else{ |
| 580 | 632 | blob_appendf(pOut, "%.80c\n", '.'); |
| @@ -597,34 +649,34 @@ | ||
| 597 | 649 | a += skip; |
| 598 | 650 | b += skip; |
| 599 | 651 | m = R[r] - skip; |
| 600 | 652 | for(j=0; j<m; j++){ |
| 601 | 653 | 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); | |
| 603 | 655 | } |
| 604 | 656 | a += m; |
| 605 | 657 | b += m; |
| 606 | 658 | |
| 607 | 659 | /* Show the differences */ |
| 608 | 660 | for(i=0; i<nr; i++){ |
| 609 | 661 | m = R[r+i*3+1]; |
| 610 | 662 | for(j=0; j<m; j++){ |
| 611 | 663 | if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html); |
| 612 | - appendDiffLine(pOut, '-', &A[a+j], html); | |
| 664 | + appendDiffLine(pOut, '-', &A[a+j], html, pRe); | |
| 613 | 665 | } |
| 614 | 666 | a += m; |
| 615 | 667 | m = R[r+i*3+2]; |
| 616 | 668 | for(j=0; j<m; j++){ |
| 617 | 669 | if( showLn ) appendDiffLineno(pOut, 0, b+j+1, html); |
| 618 | - appendDiffLine(pOut, '+', &B[b+j], html); | |
| 670 | + appendDiffLine(pOut, '+', &B[b+j], html, pRe); | |
| 619 | 671 | } |
| 620 | 672 | b += m; |
| 621 | 673 | if( i<nr-1 ){ |
| 622 | 674 | m = R[r+i*3+3]; |
| 623 | 675 | for(j=0; j<m; j++){ |
| 624 | 676 | 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); | |
| 626 | 678 | } |
| 627 | 679 | b += m; |
| 628 | 680 | a += m; |
| 629 | 681 | } |
| 630 | 682 | } |
| @@ -633,11 +685,11 @@ | ||
| 633 | 685 | assert( nr==i ); |
| 634 | 686 | m = R[r+nr*3]; |
| 635 | 687 | if( m>nContext ) m = nContext; |
| 636 | 688 | for(j=0; j<m; j++){ |
| 637 | 689 | 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); | |
| 639 | 691 | } |
| 640 | 692 | } |
| 641 | 693 | } |
| 642 | 694 | |
| 643 | 695 | /* |
| @@ -653,10 +705,11 @@ | ||
| 653 | 705 | const char *zStart; /* A <span> tag */ |
| 654 | 706 | int iEnd; /* Write </span> prior to character iEnd */ |
| 655 | 707 | int iStart2; /* Write zStart2 prior to character iStart2 */ |
| 656 | 708 | const char *zStart2; /* A <span> tag */ |
| 657 | 709 | int iEnd2; /* Write </span> prior to character iEnd2 */ |
| 710 | + ReCompiled *pRe; /* Only colorize matching lines, if not NULL */ | |
| 658 | 711 | }; |
| 659 | 712 | |
| 660 | 713 | /* |
| 661 | 714 | ** Flags for sbsWriteText() |
| 662 | 715 | */ |
| @@ -678,13 +731,17 @@ | ||
| 678 | 731 | int k; /* Cursor position */ |
| 679 | 732 | int needEndSpan = 0; |
| 680 | 733 | const char *zIn = pLine->z; |
| 681 | 734 | char *z = &p->zLine[p->n]; |
| 682 | 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 | + } | |
| 683 | 740 | for(i=j=k=0; k<w && i<n; i++, k++){ |
| 684 | 741 | char c = zIn[i]; |
| 685 | - if( p->escHtml ){ | |
| 742 | + if( colorize ){ | |
| 686 | 743 | if( i==p->iStart ){ |
| 687 | 744 | int x = strlen(p->zStart); |
| 688 | 745 | memcpy(z+j, p->zStart, x); |
| 689 | 746 | j += x; |
| 690 | 747 | needEndSpan = 1; |
| @@ -835,10 +892,41 @@ | ||
| 835 | 892 | } |
| 836 | 893 | } |
| 837 | 894 | } |
| 838 | 895 | return rc; |
| 839 | 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 | +} | |
| 840 | 928 | |
| 841 | 929 | /* |
| 842 | 930 | ** Write out lines that have been edited. Adjust the highlight to cover |
| 843 | 931 | ** only those parts of the line that actually changed. |
| 844 | 932 | */ |
| @@ -929,41 +1017,35 @@ | ||
| 929 | 1017 | && textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS) |
| 930 | 1018 | ){ |
| 931 | 1019 | sbsWriteLineno(p, lnLeft); |
| 932 | 1020 | p->iStart = nPrefix; |
| 933 | 1021 | 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 | + } | |
| 935 | 1028 | p->iStart2 = nPrefix + aLCS[1]; |
| 936 | 1029 | p->iEnd2 = nLeft - nSuffix; |
| 937 | 1030 | 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); | |
| 947 | 1032 | sbsWriteText(p, pLeft, SBS_PAD); |
| 948 | 1033 | sbsWrite(p, " | ", 3); |
| 949 | 1034 | sbsWriteLineno(p, lnRight); |
| 950 | 1035 | p->iStart = nPrefix; |
| 951 | 1036 | 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 | + } | |
| 953 | 1043 | p->iStart2 = nPrefix + aLCS[3]; |
| 954 | 1044 | p->iEnd2 = nRight - nSuffix; |
| 955 | 1045 | 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); | |
| 965 | 1047 | sbsWriteText(p, pRight, SBS_NEWLINE); |
| 966 | 1048 | return; |
| 967 | 1049 | } |
| 968 | 1050 | |
| 969 | 1051 | /* If all else fails, show a single big change between left and right */ |
| @@ -1056,13 +1138,16 @@ | ||
| 1056 | 1138 | ** |
| 1057 | 1139 | ** The return value is a buffer of unsigned characters, obtained from |
| 1058 | 1140 | ** fossil_malloc(). (The caller needs to free the return value using |
| 1059 | 1141 | ** fossil_free().) Entries in the returned array have values as follows: |
| 1060 | 1142 | ** |
| 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. | |
| 1064 | 1149 | ** |
| 1065 | 1150 | ** The length of the returned array will be just large enough to cause |
| 1066 | 1151 | ** all elements of pLeft and pRight to be consumed. |
| 1067 | 1152 | ** |
| 1068 | 1153 | ** Algorithm: Wagner's minimum edit-distance algorithm, modified by |
| @@ -1077,27 +1162,32 @@ | ||
| 1077 | 1162 | ){ |
| 1078 | 1163 | int i, j, k; /* Loop counters */ |
| 1079 | 1164 | int *a; /* One row of the Wagner matrix */ |
| 1080 | 1165 | int *pToFree; /* Space that needs to be freed */ |
| 1081 | 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) */ | |
| 1082 | 1170 | int aBuf[100]; /* Stack space for a[] if nRight not to big */ |
| 1083 | 1171 | |
| 1084 | 1172 | aM = fossil_malloc( (nLeft+1)*(nRight+1) ); |
| 1085 | 1173 | if( nLeft==0 ){ |
| 1086 | - memset(aM, 3, nRight); | |
| 1174 | + memset(aM, 2, nRight); | |
| 1087 | 1175 | return aM; |
| 1088 | 1176 | } |
| 1089 | 1177 | if( nRight==0 ){ |
| 1090 | 1178 | memset(aM, 1, nLeft); |
| 1091 | 1179 | return aM; |
| 1092 | 1180 | } |
| 1093 | 1181 | |
| 1094 | 1182 | /* This algorithm is O(N**2). So if N is too big, bail out with a |
| 1095 | 1183 | ** simple (but stupid and ugly) result that doesn't take too long. */ |
| 1184 | + mnLen = nLeft<nRight ? nLeft : nRight; | |
| 1096 | 1185 | 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); | |
| 1099 | 1189 | return aM; |
| 1100 | 1190 | } |
| 1101 | 1191 | |
| 1102 | 1192 | if( nRight < (sizeof(aBuf)/sizeof(aBuf[0]))-1 ){ |
| 1103 | 1193 | pToFree = 0; |
| @@ -1106,30 +1196,30 @@ | ||
| 1106 | 1196 | a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) ); |
| 1107 | 1197 | } |
| 1108 | 1198 | |
| 1109 | 1199 | /* Compute the best alignment */ |
| 1110 | 1200 | for(i=0; i<=nRight; i++){ |
| 1111 | - aM[i] = 3; | |
| 1201 | + aM[i] = 2; | |
| 1112 | 1202 | a[i] = i*50; |
| 1113 | 1203 | } |
| 1114 | 1204 | aM[0] = 0; |
| 1115 | 1205 | for(j=1; j<=nLeft; j++){ |
| 1116 | 1206 | int p = a[0]; |
| 1117 | 1207 | a[0] = p+50; |
| 1118 | 1208 | aM[j*(nRight+1)] = 1; |
| 1119 | 1209 | for(i=1; i<=nRight; i++){ |
| 1120 | 1210 | int m = a[i-1]+50; |
| 1121 | - int d = 3; | |
| 1211 | + int d = 2; | |
| 1122 | 1212 | if( m>a[i]+50 ){ |
| 1123 | 1213 | m = a[i]+50; |
| 1124 | 1214 | d = 1; |
| 1125 | 1215 | } |
| 1126 | 1216 | if( m>p ){ |
| 1127 | 1217 | 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 ){ | |
| 1129 | 1219 | m = p+score; |
| 1130 | - d = 2; | |
| 1220 | + d = 3 | score*4; | |
| 1131 | 1221 | } |
| 1132 | 1222 | } |
| 1133 | 1223 | p = a[i]; |
| 1134 | 1224 | a[i] = m; |
| 1135 | 1225 | aM[j*(nRight+1)+i] = d; |
| @@ -1138,28 +1228,50 @@ | ||
| 1138 | 1228 | |
| 1139 | 1229 | /* Compute the lowest-cost path back through the matrix */ |
| 1140 | 1230 | i = nRight; |
| 1141 | 1231 | j = nLeft; |
| 1142 | 1232 | k = (nRight+1)*(nLeft+1)-1; |
| 1233 | + nMatch = iMatch = 0; | |
| 1143 | 1234 | while( i+j>0 ){ |
| 1144 | - unsigned char c = aM[k--]; | |
| 1145 | - if( c==2 ){ | |
| 1235 | + unsigned char c = aM[k]; | |
| 1236 | + if( c>=3 ){ | |
| 1146 | 1237 | assert( i>0 && j>0 ); |
| 1147 | 1238 | i--; |
| 1148 | 1239 | j--; |
| 1149 | - }else if( c==3 ){ | |
| 1240 | + nMatch++; | |
| 1241 | + iMatch += (c>>2); | |
| 1242 | + aM[k] = 3; | |
| 1243 | + }else if( c==2 ){ | |
| 1150 | 1244 | assert( i>0 ); |
| 1151 | 1245 | i--; |
| 1152 | 1246 | }else{ |
| 1153 | 1247 | assert( j>0 ); |
| 1154 | 1248 | j--; |
| 1155 | 1249 | } |
| 1250 | + k--; | |
| 1156 | 1251 | aM[k] = aM[j*(nRight+1)+i]; |
| 1157 | 1252 | } |
| 1158 | 1253 | k++; |
| 1159 | 1254 | i = (nRight+1)*(nLeft+1) - k; |
| 1160 | 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 | + } | |
| 1161 | 1273 | |
| 1162 | 1274 | /* Return the result */ |
| 1163 | 1275 | fossil_free(pToFree); |
| 1164 | 1276 | return aM; |
| 1165 | 1277 | } |
| @@ -1179,13 +1291,12 @@ | ||
| 1179 | 1291 | ** in, compute a side-by-side diff into pOut. |
| 1180 | 1292 | */ |
| 1181 | 1293 | static void sbsDiff( |
| 1182 | 1294 | DContext *p, /* The computed diff */ |
| 1183 | 1295 | 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 */ | |
| 1187 | 1298 | ){ |
| 1188 | 1299 | DLine *A; /* Left side of the diff */ |
| 1189 | 1300 | DLine *B; /* Right side of the diff */ |
| 1190 | 1301 | int a = 0; /* Index of next line in A[] */ |
| 1191 | 1302 | int b = 0; /* Index of next line in B[] */ |
| @@ -1197,16 +1308,20 @@ | ||
| 1197 | 1308 | int i, j; /* Loop counters */ |
| 1198 | 1309 | int m, ma, mb;/* Number of lines to output */ |
| 1199 | 1310 | int skip; /* Number of lines to skip */ |
| 1200 | 1311 | int nChunk = 0; /* Number of chunks of diff output seen so far */ |
| 1201 | 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 */ | |
| 1202 | 1315 | |
| 1203 | 1316 | 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 ); | |
| 1205 | 1319 | 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; | |
| 1208 | 1323 | s.iStart = -1; |
| 1209 | 1324 | s.iStart2 = 0; |
| 1210 | 1325 | s.iEnd = -1; |
| 1211 | 1326 | A = p->aFrom; |
| 1212 | 1327 | B = p->aTo; |
| @@ -1215,10 +1330,35 @@ | ||
| 1215 | 1330 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| 1216 | 1331 | for(r=0; r<mxr; r += 3*nr){ |
| 1217 | 1332 | /* Figure out how many triples to show in a single block */ |
| 1218 | 1333 | for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){} |
| 1219 | 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 | + } | |
| 1220 | 1360 | |
| 1221 | 1361 | /* For the current block comprising nr triples, figure out |
| 1222 | 1362 | ** how many lines of A and B are to be displayed |
| 1223 | 1363 | */ |
| 1224 | 1364 | if( R[r]>nContext ){ |
| @@ -1243,20 +1383,21 @@ | ||
| 1243 | 1383 | na += R[r+i*3]; |
| 1244 | 1384 | nb += R[r+i*3]; |
| 1245 | 1385 | } |
| 1246 | 1386 | |
| 1247 | 1387 | /* Draw the separator between blocks */ |
| 1248 | - if( r>0 ){ | |
| 1249 | - if( escHtml ){ | |
| 1388 | + if( showDivider ){ | |
| 1389 | + if( s.escHtml ){ | |
| 1250 | 1390 | blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n", |
| 1251 | - width*2+16, '.'); | |
| 1391 | + s.width*2+16, '.'); | |
| 1252 | 1392 | }else{ |
| 1253 | - blob_appendf(pOut, "%.*c\n", width*2+16, '.'); | |
| 1393 | + blob_appendf(pOut, "%.*c\n", s.width*2+16, '.'); | |
| 1254 | 1394 | } |
| 1255 | 1395 | } |
| 1396 | + showDivider = 1; | |
| 1256 | 1397 | nChunk++; |
| 1257 | - if( escHtml ){ | |
| 1398 | + if( s.escHtml ){ | |
| 1258 | 1399 | blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk); |
| 1259 | 1400 | } |
| 1260 | 1401 | |
| 1261 | 1402 | /* Show the initial common area */ |
| 1262 | 1403 | a += skip; |
| @@ -1292,38 +1433,41 @@ | ||
| 1292 | 1433 | } |
| 1293 | 1434 | |
| 1294 | 1435 | alignment = sbsAlignment(&A[a], ma, &B[b], mb); |
| 1295 | 1436 | for(j=0; ma+mb>0; j++){ |
| 1296 | 1437 | if( alignment[j]==1 ){ |
| 1438 | + /* Delete one line from the left */ | |
| 1297 | 1439 | s.n = 0; |
| 1298 | 1440 | sbsWriteLineno(&s, a); |
| 1299 | 1441 | s.iStart = 0; |
| 1300 | 1442 | s.zStart = "<span class=\"diffrm\">"; |
| 1301 | 1443 | s.iEnd = s.width; |
| 1302 | 1444 | sbsWriteText(&s, &A[a], SBS_PAD); |
| 1303 | - if( escHtml ){ | |
| 1445 | + if( s.escHtml ){ | |
| 1304 | 1446 | sbsWrite(&s, " <\n", 6); |
| 1305 | 1447 | }else{ |
| 1306 | 1448 | sbsWrite(&s, " <\n", 3); |
| 1307 | 1449 | } |
| 1308 | 1450 | blob_append(pOut, s.zLine, s.n); |
| 1309 | 1451 | assert( ma>0 ); |
| 1310 | 1452 | ma--; |
| 1311 | 1453 | a++; |
| 1312 | - }else if( alignment[j]==2 ){ | |
| 1454 | + }else if( alignment[j]==3 ){ | |
| 1455 | + /* The left line is changed into the right line */ | |
| 1313 | 1456 | s.n = 0; |
| 1314 | 1457 | sbsWriteLineChange(&s, &A[a], a, &B[b], b); |
| 1315 | 1458 | blob_append(pOut, s.zLine, s.n); |
| 1316 | 1459 | assert( ma>0 && mb>0 ); |
| 1317 | 1460 | ma--; |
| 1318 | 1461 | mb--; |
| 1319 | 1462 | a++; |
| 1320 | 1463 | b++; |
| 1321 | - }else{ | |
| 1464 | + }else if( alignment[j]==2 ){ | |
| 1465 | + /* Insert one line on the right */ | |
| 1322 | 1466 | s.n = 0; |
| 1323 | - sbsWriteSpace(&s, width + 7); | |
| 1324 | - if( escHtml ){ | |
| 1467 | + sbsWriteSpace(&s, s.width + 7); | |
| 1468 | + if( s.escHtml ){ | |
| 1325 | 1469 | sbsWrite(&s, " > ", 6); |
| 1326 | 1470 | }else{ |
| 1327 | 1471 | sbsWrite(&s, " > ", 3); |
| 1328 | 1472 | } |
| 1329 | 1473 | sbsWriteLineno(&s, b); |
| @@ -1333,11 +1477,31 @@ | ||
| 1333 | 1477 | sbsWriteText(&s, &B[b], SBS_NEWLINE); |
| 1334 | 1478 | blob_append(pOut, s.zLine, s.n); |
| 1335 | 1479 | assert( mb>0 ); |
| 1336 | 1480 | mb--; |
| 1337 | 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++; | |
| 1338 | 1501 | } |
| 1502 | + | |
| 1339 | 1503 | } |
| 1340 | 1504 | fossil_free(alignment); |
| 1341 | 1505 | if( i<nr-1 ){ |
| 1342 | 1506 | m = R[r+i*3+3]; |
| 1343 | 1507 | for(j=0; j<m; j++){ |
| @@ -1761,11 +1925,11 @@ | ||
| 1761 | 1925 | ** Extract the number of lines of context from diffFlags. Supply an |
| 1762 | 1926 | ** appropriate default if no context width is specified. |
| 1763 | 1927 | */ |
| 1764 | 1928 | int diff_context_lines(u64 diffFlags){ |
| 1765 | 1929 | int n = diffFlags & DIFF_CONTEXT_MASK; |
| 1766 | - if( n==0 ) n = 5; | |
| 1930 | + if( n==0 && (diffFlags & DIFF_CONTEXT_EX)==0 ) n = 5; | |
| 1767 | 1931 | return n; |
| 1768 | 1932 | } |
| 1769 | 1933 | |
| 1770 | 1934 | /* |
| 1771 | 1935 | ** Extract the width of columns for side-by-side diff. Supply an |
| @@ -1793,22 +1957,21 @@ | ||
| 1793 | 1957 | */ |
| 1794 | 1958 | int *text_diff( |
| 1795 | 1959 | Blob *pA_Blob, /* FROM file */ |
| 1796 | 1960 | Blob *pB_Blob, /* TO file */ |
| 1797 | 1961 | Blob *pOut, /* Write diff here if not NULL */ |
| 1962 | + ReCompiled *pRe, /* Only output changes where this Regexp matches */ | |
| 1798 | 1963 | u64 diffFlags /* DIFF_* flags defined above */ |
| 1799 | 1964 | ){ |
| 1800 | 1965 | int ignoreEolWs; /* Ignore whitespace at the end of lines */ |
| 1801 | - int nContext; /* Amount of context to display */ | |
| 1802 | 1966 | DContext c; |
| 1803 | 1967 | |
| 1804 | 1968 | if( diffFlags & DIFF_INVERT ){ |
| 1805 | 1969 | Blob *pTemp = pA_Blob; |
| 1806 | 1970 | pA_Blob = pB_Blob; |
| 1807 | 1971 | pB_Blob = pTemp; |
| 1808 | 1972 | } |
| 1809 | - nContext = diff_context_lines(diffFlags); | |
| 1810 | 1973 | ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0; |
| 1811 | 1974 | |
| 1812 | 1975 | /* Prepare the input files */ |
| 1813 | 1976 | memset(&c, 0, sizeof(c)); |
| 1814 | 1977 | c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), |
| @@ -1828,17 +1991,14 @@ | ||
| 1828 | 1991 | diff_all(&c); |
| 1829 | 1992 | if( (diffFlags & DIFF_NOOPT)==0 ) diff_optimize(&c); |
| 1830 | 1993 | |
| 1831 | 1994 | if( pOut ){ |
| 1832 | 1995 | /* Compute a context or side-by-side diff into pOut */ |
| 1833 | - int escHtml = (diffFlags & DIFF_HTML)!=0; | |
| 1834 | 1996 | if( diffFlags & DIFF_SIDEBYSIDE ){ |
| 1835 | - int width = diff_width(diffFlags); | |
| 1836 | - sbsDiff(&c, pOut, nContext, width, escHtml); | |
| 1997 | + sbsDiff(&c, pOut, pRe, diffFlags); | |
| 1837 | 1998 | }else{ |
| 1838 | - int showLn = (diffFlags & DIFF_LINENO)!=0; | |
| 1839 | - contextDiff(&c, pOut, nContext, showLn, escHtml); | |
| 1999 | + contextDiff(&c, pOut, pRe, diffFlags); | |
| 1840 | 2000 | } |
| 1841 | 2001 | fossil_free(c.aFrom); |
| 1842 | 2002 | fossil_free(c.aTo); |
| 1843 | 2003 | fossil_free(c.aEdit); |
| 1844 | 2004 | return 0; |
| @@ -1864,19 +2024,19 @@ | ||
| 1864 | 2024 | ** --noopt Disable optimization DIFF_NOOPT |
| 1865 | 2025 | ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE |
| 1866 | 2026 | ** --unified Unified diff. ~DIFF_SIDEBYSIDE |
| 1867 | 2027 | ** --width|-W N N character lines. DIFF_WIDTH_MASK |
| 1868 | 2028 | */ |
| 1869 | -int diff_options(void){ | |
| 2029 | +u64 diff_options(void){ | |
| 1870 | 2030 | u64 diffFlags = 0; |
| 1871 | 2031 | const char *z; |
| 1872 | 2032 | int f; |
| 1873 | 2033 | if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE; |
| 1874 | 2034 | 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 ){ | |
| 1876 | 2036 | if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK; |
| 1877 | - diffFlags |= f; | |
| 2037 | + diffFlags |= f + DIFF_CONTEXT_EX; | |
| 1878 | 2038 | } |
| 1879 | 2039 | if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){ |
| 1880 | 2040 | f *= DIFF_CONTEXT_MASK+1; |
| 1881 | 2041 | if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK; |
| 1882 | 2042 | diffFlags |= f; |
| @@ -1901,34 +2061,52 @@ | ||
| 1901 | 2061 | if( g.argc<4 ) usage("FILE1 FILE2 ..."); |
| 1902 | 2062 | blob_read_from_file(&a, g.argv[2]); |
| 1903 | 2063 | for(i=3; i<g.argc; i++){ |
| 1904 | 2064 | if( i>3 ) fossil_print("-------------------------------\n"); |
| 1905 | 2065 | 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); | |
| 1907 | 2067 | for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ |
| 1908 | 2068 | fossil_print(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]); |
| 1909 | 2069 | } |
| 1910 | 2070 | /* free(R); */ |
| 1911 | 2071 | blob_reset(&b); |
| 1912 | 2072 | } |
| 1913 | 2073 | } |
| 1914 | 2074 | |
| 1915 | 2075 | /* |
| 1916 | -** COMMAND: test-udiff | |
| 2076 | +** COMMAND: test-diff | |
| 2077 | +** | |
| 2078 | +** Usage: %fossil [options] FILE1 FILE2 | |
| 1917 | 2079 | ** |
| 1918 | 2080 | ** Print the difference between two files. The usual diff options apply. |
| 1919 | 2081 | */ |
| 1920 | -void test_udiff_cmd(void){ | |
| 2082 | +void test_diff_cmd(void){ | |
| 1921 | 2083 | 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 */ | |
| 1923 | 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(); | |
| 1924 | 2100 | if( g.argc!=4 ) usage("FILE1 FILE2"); |
| 2101 | + diff_print_filenames(g.argv[2], g.argv[3], diffFlag); | |
| 1925 | 2102 | blob_read_from_file(&a, g.argv[2]); |
| 1926 | 2103 | blob_read_from_file(&b, g.argv[3]); |
| 1927 | 2104 | blob_zero(&out); |
| 1928 | - text_diff(&a, &b, &out, diffFlag); | |
| 2105 | + text_diff(&a, &b, &out, pRe, diffFlag); | |
| 1929 | 2106 | blob_write_to_file(&out, "-"); |
| 2107 | + re_free(pRe); | |
| 1930 | 2108 | } |
| 1931 | 2109 | |
| 1932 | 2110 | /************************************************************************** |
| 1933 | 2111 | ** The basic difference engine is above. What follows is the annotation |
| 1934 | 2112 | ** engine. Both are in the same file since they share many components. |
| 1935 | 2113 |
| --- 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, " <\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, " > ", 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, " <\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, " > ", 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 @@ | ||
| 23 | 23 | #include <assert.h> |
| 24 | 24 | |
| 25 | 25 | |
| 26 | 26 | #if INTERFACE |
| 27 | 27 | /* |
| 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. | |
| 29 | 30 | */ |
| 30 | 31 | #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */ |
| 31 | 32 | #define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */ |
| 32 | 33 | #define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */ |
| 33 | 34 | #define DIFF_SIDEBYSIDE ((u64)0x02000000) /* Generate a side-by-side diff */ |
| @@ -37,10 +38,11 @@ | ||
| 37 | 38 | #define DIFF_HTML ((u64)0x10000000) /* Render for HTML */ |
| 38 | 39 | #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */ |
| 39 | 40 | #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */ |
| 40 | 41 | #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */ |
| 41 | 42 | #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */ |
| 43 | +#define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */ | |
| 42 | 44 | |
| 43 | 45 | /* |
| 44 | 46 | ** These error messages are shared in multiple locations. They are defined |
| 45 | 47 | ** here for consistency. |
| 46 | 48 | */ |
| @@ -52,11 +54,11 @@ | ||
| 52 | 54 | |
| 53 | 55 | #define looks_like_binary(blob) ((looks_like_utf8((blob))&3) == 0) |
| 54 | 56 | #endif /* INTERFACE */ |
| 55 | 57 | |
| 56 | 58 | /* |
| 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) | |
| 58 | 60 | */ |
| 59 | 61 | #define LENGTH_MASK_SZ 13 |
| 60 | 62 | #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1) |
| 61 | 63 | |
| 62 | 64 | /* |
| @@ -116,10 +118,13 @@ | ||
| 116 | 118 | ** more. If trailing whitespace is ignored, the "patch" command gets |
| 117 | 119 | ** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b] |
| 118 | 120 | ** |
| 119 | 121 | ** Return 0 if the file is binary or contains a line that is |
| 120 | 122 | ** too long. |
| 123 | +** | |
| 124 | +** Profiling show that in most cases this routine consumes the bulk of | |
| 125 | +** the CPU time on a diff. | |
| 121 | 126 | */ |
| 122 | 127 | static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){ |
| 123 | 128 | int nLine, i, j, k, x; |
| 124 | 129 | unsigned int h, h2; |
| 125 | 130 | DLine *a; |
| @@ -445,34 +450,50 @@ | ||
| 445 | 450 | ** Return true if two DLine elements are identical. |
| 446 | 451 | */ |
| 447 | 452 | static int same_dline(DLine *pA, DLine *pB){ |
| 448 | 453 | return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0; |
| 449 | 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 | +} | |
| 450 | 473 | |
| 451 | 474 | /* |
| 452 | 475 | ** Append a single line of context-diff output to pOut. |
| 453 | 476 | */ |
| 454 | 477 | static void appendDiffLine( |
| 455 | 478 | Blob *pOut, /* Where to write the line of output */ |
| 456 | 479 | char cPrefix, /* One of " ", "+", or "-" */ |
| 457 | 480 | 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 */ | |
| 459 | 483 | ){ |
| 460 | - int i; | |
| 461 | 484 | blob_append(pOut, &cPrefix, 1); |
| 462 | 485 | if( html ){ |
| 463 | 486 | char *zHtml; |
| 464 | - if( cPrefix=='+' ){ | |
| 487 | + if( pRe && re_dline_match(pRe, pLine, 1)==0 ){ | |
| 488 | + cPrefix = ' '; | |
| 489 | + }else if( cPrefix=='+' ){ | |
| 465 | 490 | blob_append(pOut, "<span class=\"diffadd\">", -1); |
| 466 | 491 | }else if( cPrefix=='-' ){ |
| 467 | 492 | blob_append(pOut, "<span class=\"diffrm\">", -1); |
| 468 | 493 | } |
| 469 | 494 | 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 | 495 | blob_append(pOut, zHtml, -1); |
| 475 | 496 | fossil_free(zHtml); |
| 476 | 497 | if( cPrefix!=' ' ){ |
| 477 | 498 | blob_append(pOut, "</span>", -1); |
| 478 | 499 | } |
| @@ -482,11 +503,11 @@ | ||
| 482 | 503 | blob_append(pOut, "\n", 1); |
| 483 | 504 | } |
| 484 | 505 | |
| 485 | 506 | /* |
| 486 | 507 | ** 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 | |
| 488 | 509 | ** to leave that number field blank. The "html" parameter means to format |
| 489 | 510 | ** the output for HTML. |
| 490 | 511 | */ |
| 491 | 512 | static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){ |
| 492 | 513 | if( html ) blob_append(pOut, "<span class=\"diffln\">", -1); |
| @@ -501,21 +522,19 @@ | ||
| 501 | 522 | blob_append(pOut, " ", 8); |
| 502 | 523 | } |
| 503 | 524 | if( html ) blob_append(pOut, "</span>", -1); |
| 504 | 525 | } |
| 505 | 526 | |
| 506 | - | |
| 507 | 527 | /* |
| 508 | 528 | ** Given a raw diff p[] in which the p->aEdit[] array has been filled |
| 509 | 529 | ** in, compute a context diff into pOut. |
| 510 | 530 | */ |
| 511 | 531 | static void contextDiff( |
| 512 | 532 | DContext *p, /* The difference */ |
| 513 | 533 | 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 */ | |
| 517 | 536 | ){ |
| 518 | 537 | DLine *A; /* Left side of the diff */ |
| 519 | 538 | DLine *B; /* Right side of the diff */ |
| 520 | 539 | int a = 0; /* Index of next line in A[] */ |
| 521 | 540 | int b = 0; /* Index of next line in B[] */ |
| @@ -526,11 +545,18 @@ | ||
| 526 | 545 | int na, nb; /* Number of lines shown from A and B */ |
| 527 | 546 | int i, j; /* Loop counters */ |
| 528 | 547 | int m; /* Number of lines to output */ |
| 529 | 548 | int skip; /* Number of lines to skip */ |
| 530 | 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 */ | |
| 531 | 554 | |
| 555 | + nContext = diff_context_lines(diffFlags); | |
| 556 | + showLn = (diffFlags & DIFF_LINENO)!=0; | |
| 557 | + html = (diffFlags & DIFF_HTML)!=0; | |
| 532 | 558 | A = p->aFrom; |
| 533 | 559 | B = p->aTo; |
| 534 | 560 | R = p->aEdit; |
| 535 | 561 | mxr = p->nEdit; |
| 536 | 562 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| @@ -537,10 +563,35 @@ | ||
| 537 | 563 | for(r=0; r<mxr; r += 3*nr){ |
| 538 | 564 | /* Figure out how many triples to show in a single block */ |
| 539 | 565 | for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){} |
| 540 | 566 | /* printf("r=%d nr=%d\n", r, nr); */ |
| 541 | 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 | + | |
| 542 | 593 | /* For the current block comprising nr triples, figure out |
| 543 | 594 | ** how many lines of A and B are to be displayed |
| 544 | 595 | */ |
| 545 | 596 | if( R[r]>nContext ){ |
| 546 | 597 | na = nb = nContext; |
| @@ -564,17 +615,18 @@ | ||
| 564 | 615 | na += R[r+i*3]; |
| 565 | 616 | nb += R[r+i*3]; |
| 566 | 617 | } |
| 567 | 618 | |
| 568 | 619 | /* 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 | |
| 570 | 621 | ** the previous block. |
| 571 | 622 | */ |
| 572 | 623 | nChunk++; |
| 573 | 624 | if( showLn ){ |
| 574 | - if( r==0 ){ | |
| 625 | + if( !showDivider ){ | |
| 575 | 626 | /* Do not show a top divider */ |
| 627 | + showDivider = 1; | |
| 576 | 628 | }else if( html ){ |
| 577 | 629 | blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.'); |
| 578 | 630 | blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk); |
| 579 | 631 | }else{ |
| 580 | 632 | blob_appendf(pOut, "%.80c\n", '.'); |
| @@ -597,34 +649,34 @@ | ||
| 597 | 649 | a += skip; |
| 598 | 650 | b += skip; |
| 599 | 651 | m = R[r] - skip; |
| 600 | 652 | for(j=0; j<m; j++){ |
| 601 | 653 | 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); | |
| 603 | 655 | } |
| 604 | 656 | a += m; |
| 605 | 657 | b += m; |
| 606 | 658 | |
| 607 | 659 | /* Show the differences */ |
| 608 | 660 | for(i=0; i<nr; i++){ |
| 609 | 661 | m = R[r+i*3+1]; |
| 610 | 662 | for(j=0; j<m; j++){ |
| 611 | 663 | if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html); |
| 612 | - appendDiffLine(pOut, '-', &A[a+j], html); | |
| 664 | + appendDiffLine(pOut, '-', &A[a+j], html, pRe); | |
| 613 | 665 | } |
| 614 | 666 | a += m; |
| 615 | 667 | m = R[r+i*3+2]; |
| 616 | 668 | for(j=0; j<m; j++){ |
| 617 | 669 | if( showLn ) appendDiffLineno(pOut, 0, b+j+1, html); |
| 618 | - appendDiffLine(pOut, '+', &B[b+j], html); | |
| 670 | + appendDiffLine(pOut, '+', &B[b+j], html, pRe); | |
| 619 | 671 | } |
| 620 | 672 | b += m; |
| 621 | 673 | if( i<nr-1 ){ |
| 622 | 674 | m = R[r+i*3+3]; |
| 623 | 675 | for(j=0; j<m; j++){ |
| 624 | 676 | 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); | |
| 626 | 678 | } |
| 627 | 679 | b += m; |
| 628 | 680 | a += m; |
| 629 | 681 | } |
| 630 | 682 | } |
| @@ -633,11 +685,11 @@ | ||
| 633 | 685 | assert( nr==i ); |
| 634 | 686 | m = R[r+nr*3]; |
| 635 | 687 | if( m>nContext ) m = nContext; |
| 636 | 688 | for(j=0; j<m; j++){ |
| 637 | 689 | 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); | |
| 639 | 691 | } |
| 640 | 692 | } |
| 641 | 693 | } |
| 642 | 694 | |
| 643 | 695 | /* |
| @@ -653,10 +705,11 @@ | ||
| 653 | 705 | const char *zStart; /* A <span> tag */ |
| 654 | 706 | int iEnd; /* Write </span> prior to character iEnd */ |
| 655 | 707 | int iStart2; /* Write zStart2 prior to character iStart2 */ |
| 656 | 708 | const char *zStart2; /* A <span> tag */ |
| 657 | 709 | int iEnd2; /* Write </span> prior to character iEnd2 */ |
| 710 | + ReCompiled *pRe; /* Only colorize matching lines, if not NULL */ | |
| 658 | 711 | }; |
| 659 | 712 | |
| 660 | 713 | /* |
| 661 | 714 | ** Flags for sbsWriteText() |
| 662 | 715 | */ |
| @@ -678,13 +731,17 @@ | ||
| 678 | 731 | int k; /* Cursor position */ |
| 679 | 732 | int needEndSpan = 0; |
| 680 | 733 | const char *zIn = pLine->z; |
| 681 | 734 | char *z = &p->zLine[p->n]; |
| 682 | 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 | + } | |
| 683 | 740 | for(i=j=k=0; k<w && i<n; i++, k++){ |
| 684 | 741 | char c = zIn[i]; |
| 685 | - if( p->escHtml ){ | |
| 742 | + if( colorize ){ | |
| 686 | 743 | if( i==p->iStart ){ |
| 687 | 744 | int x = strlen(p->zStart); |
| 688 | 745 | memcpy(z+j, p->zStart, x); |
| 689 | 746 | j += x; |
| 690 | 747 | needEndSpan = 1; |
| @@ -835,10 +892,41 @@ | ||
| 835 | 892 | } |
| 836 | 893 | } |
| 837 | 894 | } |
| 838 | 895 | return rc; |
| 839 | 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 | +} | |
| 840 | 928 | |
| 841 | 929 | /* |
| 842 | 930 | ** Write out lines that have been edited. Adjust the highlight to cover |
| 843 | 931 | ** only those parts of the line that actually changed. |
| 844 | 932 | */ |
| @@ -929,41 +1017,35 @@ | ||
| 929 | 1017 | && textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS) |
| 930 | 1018 | ){ |
| 931 | 1019 | sbsWriteLineno(p, lnLeft); |
| 932 | 1020 | p->iStart = nPrefix; |
| 933 | 1021 | 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 | + } | |
| 935 | 1028 | p->iStart2 = nPrefix + aLCS[1]; |
| 936 | 1029 | p->iEnd2 = nLeft - nSuffix; |
| 937 | 1030 | 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); | |
| 947 | 1032 | sbsWriteText(p, pLeft, SBS_PAD); |
| 948 | 1033 | sbsWrite(p, " | ", 3); |
| 949 | 1034 | sbsWriteLineno(p, lnRight); |
| 950 | 1035 | p->iStart = nPrefix; |
| 951 | 1036 | 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 | + } | |
| 953 | 1043 | p->iStart2 = nPrefix + aLCS[3]; |
| 954 | 1044 | p->iEnd2 = nRight - nSuffix; |
| 955 | 1045 | 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); | |
| 965 | 1047 | sbsWriteText(p, pRight, SBS_NEWLINE); |
| 966 | 1048 | return; |
| 967 | 1049 | } |
| 968 | 1050 | |
| 969 | 1051 | /* If all else fails, show a single big change between left and right */ |
| @@ -1056,13 +1138,16 @@ | ||
| 1056 | 1138 | ** |
| 1057 | 1139 | ** The return value is a buffer of unsigned characters, obtained from |
| 1058 | 1140 | ** fossil_malloc(). (The caller needs to free the return value using |
| 1059 | 1141 | ** fossil_free().) Entries in the returned array have values as follows: |
| 1060 | 1142 | ** |
| 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. | |
| 1064 | 1149 | ** |
| 1065 | 1150 | ** The length of the returned array will be just large enough to cause |
| 1066 | 1151 | ** all elements of pLeft and pRight to be consumed. |
| 1067 | 1152 | ** |
| 1068 | 1153 | ** Algorithm: Wagner's minimum edit-distance algorithm, modified by |
| @@ -1077,27 +1162,32 @@ | ||
| 1077 | 1162 | ){ |
| 1078 | 1163 | int i, j, k; /* Loop counters */ |
| 1079 | 1164 | int *a; /* One row of the Wagner matrix */ |
| 1080 | 1165 | int *pToFree; /* Space that needs to be freed */ |
| 1081 | 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) */ | |
| 1082 | 1170 | int aBuf[100]; /* Stack space for a[] if nRight not to big */ |
| 1083 | 1171 | |
| 1084 | 1172 | aM = fossil_malloc( (nLeft+1)*(nRight+1) ); |
| 1085 | 1173 | if( nLeft==0 ){ |
| 1086 | - memset(aM, 3, nRight); | |
| 1174 | + memset(aM, 2, nRight); | |
| 1087 | 1175 | return aM; |
| 1088 | 1176 | } |
| 1089 | 1177 | if( nRight==0 ){ |
| 1090 | 1178 | memset(aM, 1, nLeft); |
| 1091 | 1179 | return aM; |
| 1092 | 1180 | } |
| 1093 | 1181 | |
| 1094 | 1182 | /* This algorithm is O(N**2). So if N is too big, bail out with a |
| 1095 | 1183 | ** simple (but stupid and ugly) result that doesn't take too long. */ |
| 1184 | + mnLen = nLeft<nRight ? nLeft : nRight; | |
| 1096 | 1185 | 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); | |
| 1099 | 1189 | return aM; |
| 1100 | 1190 | } |
| 1101 | 1191 | |
| 1102 | 1192 | if( nRight < (sizeof(aBuf)/sizeof(aBuf[0]))-1 ){ |
| 1103 | 1193 | pToFree = 0; |
| @@ -1106,30 +1196,30 @@ | ||
| 1106 | 1196 | a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) ); |
| 1107 | 1197 | } |
| 1108 | 1198 | |
| 1109 | 1199 | /* Compute the best alignment */ |
| 1110 | 1200 | for(i=0; i<=nRight; i++){ |
| 1111 | - aM[i] = 3; | |
| 1201 | + aM[i] = 2; | |
| 1112 | 1202 | a[i] = i*50; |
| 1113 | 1203 | } |
| 1114 | 1204 | aM[0] = 0; |
| 1115 | 1205 | for(j=1; j<=nLeft; j++){ |
| 1116 | 1206 | int p = a[0]; |
| 1117 | 1207 | a[0] = p+50; |
| 1118 | 1208 | aM[j*(nRight+1)] = 1; |
| 1119 | 1209 | for(i=1; i<=nRight; i++){ |
| 1120 | 1210 | int m = a[i-1]+50; |
| 1121 | - int d = 3; | |
| 1211 | + int d = 2; | |
| 1122 | 1212 | if( m>a[i]+50 ){ |
| 1123 | 1213 | m = a[i]+50; |
| 1124 | 1214 | d = 1; |
| 1125 | 1215 | } |
| 1126 | 1216 | if( m>p ){ |
| 1127 | 1217 | 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 ){ | |
| 1129 | 1219 | m = p+score; |
| 1130 | - d = 2; | |
| 1220 | + d = 3 | score*4; | |
| 1131 | 1221 | } |
| 1132 | 1222 | } |
| 1133 | 1223 | p = a[i]; |
| 1134 | 1224 | a[i] = m; |
| 1135 | 1225 | aM[j*(nRight+1)+i] = d; |
| @@ -1138,28 +1228,50 @@ | ||
| 1138 | 1228 | |
| 1139 | 1229 | /* Compute the lowest-cost path back through the matrix */ |
| 1140 | 1230 | i = nRight; |
| 1141 | 1231 | j = nLeft; |
| 1142 | 1232 | k = (nRight+1)*(nLeft+1)-1; |
| 1233 | + nMatch = iMatch = 0; | |
| 1143 | 1234 | while( i+j>0 ){ |
| 1144 | - unsigned char c = aM[k--]; | |
| 1145 | - if( c==2 ){ | |
| 1235 | + unsigned char c = aM[k]; | |
| 1236 | + if( c>=3 ){ | |
| 1146 | 1237 | assert( i>0 && j>0 ); |
| 1147 | 1238 | i--; |
| 1148 | 1239 | j--; |
| 1149 | - }else if( c==3 ){ | |
| 1240 | + nMatch++; | |
| 1241 | + iMatch += (c>>2); | |
| 1242 | + aM[k] = 3; | |
| 1243 | + }else if( c==2 ){ | |
| 1150 | 1244 | assert( i>0 ); |
| 1151 | 1245 | i--; |
| 1152 | 1246 | }else{ |
| 1153 | 1247 | assert( j>0 ); |
| 1154 | 1248 | j--; |
| 1155 | 1249 | } |
| 1250 | + k--; | |
| 1156 | 1251 | aM[k] = aM[j*(nRight+1)+i]; |
| 1157 | 1252 | } |
| 1158 | 1253 | k++; |
| 1159 | 1254 | i = (nRight+1)*(nLeft+1) - k; |
| 1160 | 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 | + } | |
| 1161 | 1273 | |
| 1162 | 1274 | /* Return the result */ |
| 1163 | 1275 | fossil_free(pToFree); |
| 1164 | 1276 | return aM; |
| 1165 | 1277 | } |
| @@ -1179,13 +1291,12 @@ | ||
| 1179 | 1291 | ** in, compute a side-by-side diff into pOut. |
| 1180 | 1292 | */ |
| 1181 | 1293 | static void sbsDiff( |
| 1182 | 1294 | DContext *p, /* The computed diff */ |
| 1183 | 1295 | 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 */ | |
| 1187 | 1298 | ){ |
| 1188 | 1299 | DLine *A; /* Left side of the diff */ |
| 1189 | 1300 | DLine *B; /* Right side of the diff */ |
| 1190 | 1301 | int a = 0; /* Index of next line in A[] */ |
| 1191 | 1302 | int b = 0; /* Index of next line in B[] */ |
| @@ -1197,16 +1308,20 @@ | ||
| 1197 | 1308 | int i, j; /* Loop counters */ |
| 1198 | 1309 | int m, ma, mb;/* Number of lines to output */ |
| 1199 | 1310 | int skip; /* Number of lines to skip */ |
| 1200 | 1311 | int nChunk = 0; /* Number of chunks of diff output seen so far */ |
| 1201 | 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 */ | |
| 1202 | 1315 | |
| 1203 | 1316 | 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 ); | |
| 1205 | 1319 | 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; | |
| 1208 | 1323 | s.iStart = -1; |
| 1209 | 1324 | s.iStart2 = 0; |
| 1210 | 1325 | s.iEnd = -1; |
| 1211 | 1326 | A = p->aFrom; |
| 1212 | 1327 | B = p->aTo; |
| @@ -1215,10 +1330,35 @@ | ||
| 1215 | 1330 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| 1216 | 1331 | for(r=0; r<mxr; r += 3*nr){ |
| 1217 | 1332 | /* Figure out how many triples to show in a single block */ |
| 1218 | 1333 | for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){} |
| 1219 | 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 | + } | |
| 1220 | 1360 | |
| 1221 | 1361 | /* For the current block comprising nr triples, figure out |
| 1222 | 1362 | ** how many lines of A and B are to be displayed |
| 1223 | 1363 | */ |
| 1224 | 1364 | if( R[r]>nContext ){ |
| @@ -1243,20 +1383,21 @@ | ||
| 1243 | 1383 | na += R[r+i*3]; |
| 1244 | 1384 | nb += R[r+i*3]; |
| 1245 | 1385 | } |
| 1246 | 1386 | |
| 1247 | 1387 | /* Draw the separator between blocks */ |
| 1248 | - if( r>0 ){ | |
| 1249 | - if( escHtml ){ | |
| 1388 | + if( showDivider ){ | |
| 1389 | + if( s.escHtml ){ | |
| 1250 | 1390 | blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n", |
| 1251 | - width*2+16, '.'); | |
| 1391 | + s.width*2+16, '.'); | |
| 1252 | 1392 | }else{ |
| 1253 | - blob_appendf(pOut, "%.*c\n", width*2+16, '.'); | |
| 1393 | + blob_appendf(pOut, "%.*c\n", s.width*2+16, '.'); | |
| 1254 | 1394 | } |
| 1255 | 1395 | } |
| 1396 | + showDivider = 1; | |
| 1256 | 1397 | nChunk++; |
| 1257 | - if( escHtml ){ | |
| 1398 | + if( s.escHtml ){ | |
| 1258 | 1399 | blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk); |
| 1259 | 1400 | } |
| 1260 | 1401 | |
| 1261 | 1402 | /* Show the initial common area */ |
| 1262 | 1403 | a += skip; |
| @@ -1292,38 +1433,41 @@ | ||
| 1292 | 1433 | } |
| 1293 | 1434 | |
| 1294 | 1435 | alignment = sbsAlignment(&A[a], ma, &B[b], mb); |
| 1295 | 1436 | for(j=0; ma+mb>0; j++){ |
| 1296 | 1437 | if( alignment[j]==1 ){ |
| 1438 | + /* Delete one line from the left */ | |
| 1297 | 1439 | s.n = 0; |
| 1298 | 1440 | sbsWriteLineno(&s, a); |
| 1299 | 1441 | s.iStart = 0; |
| 1300 | 1442 | s.zStart = "<span class=\"diffrm\">"; |
| 1301 | 1443 | s.iEnd = s.width; |
| 1302 | 1444 | sbsWriteText(&s, &A[a], SBS_PAD); |
| 1303 | - if( escHtml ){ | |
| 1445 | + if( s.escHtml ){ | |
| 1304 | 1446 | sbsWrite(&s, " <\n", 6); |
| 1305 | 1447 | }else{ |
| 1306 | 1448 | sbsWrite(&s, " <\n", 3); |
| 1307 | 1449 | } |
| 1308 | 1450 | blob_append(pOut, s.zLine, s.n); |
| 1309 | 1451 | assert( ma>0 ); |
| 1310 | 1452 | ma--; |
| 1311 | 1453 | a++; |
| 1312 | - }else if( alignment[j]==2 ){ | |
| 1454 | + }else if( alignment[j]==3 ){ | |
| 1455 | + /* The left line is changed into the right line */ | |
| 1313 | 1456 | s.n = 0; |
| 1314 | 1457 | sbsWriteLineChange(&s, &A[a], a, &B[b], b); |
| 1315 | 1458 | blob_append(pOut, s.zLine, s.n); |
| 1316 | 1459 | assert( ma>0 && mb>0 ); |
| 1317 | 1460 | ma--; |
| 1318 | 1461 | mb--; |
| 1319 | 1462 | a++; |
| 1320 | 1463 | b++; |
| 1321 | - }else{ | |
| 1464 | + }else if( alignment[j]==2 ){ | |
| 1465 | + /* Insert one line on the right */ | |
| 1322 | 1466 | s.n = 0; |
| 1323 | - sbsWriteSpace(&s, width + 7); | |
| 1324 | - if( escHtml ){ | |
| 1467 | + sbsWriteSpace(&s, s.width + 7); | |
| 1468 | + if( s.escHtml ){ | |
| 1325 | 1469 | sbsWrite(&s, " > ", 6); |
| 1326 | 1470 | }else{ |
| 1327 | 1471 | sbsWrite(&s, " > ", 3); |
| 1328 | 1472 | } |
| 1329 | 1473 | sbsWriteLineno(&s, b); |
| @@ -1333,11 +1477,31 @@ | ||
| 1333 | 1477 | sbsWriteText(&s, &B[b], SBS_NEWLINE); |
| 1334 | 1478 | blob_append(pOut, s.zLine, s.n); |
| 1335 | 1479 | assert( mb>0 ); |
| 1336 | 1480 | mb--; |
| 1337 | 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++; | |
| 1338 | 1501 | } |
| 1502 | + | |
| 1339 | 1503 | } |
| 1340 | 1504 | fossil_free(alignment); |
| 1341 | 1505 | if( i<nr-1 ){ |
| 1342 | 1506 | m = R[r+i*3+3]; |
| 1343 | 1507 | for(j=0; j<m; j++){ |
| @@ -1761,11 +1925,11 @@ | ||
| 1761 | 1925 | ** Extract the number of lines of context from diffFlags. Supply an |
| 1762 | 1926 | ** appropriate default if no context width is specified. |
| 1763 | 1927 | */ |
| 1764 | 1928 | int diff_context_lines(u64 diffFlags){ |
| 1765 | 1929 | int n = diffFlags & DIFF_CONTEXT_MASK; |
| 1766 | - if( n==0 ) n = 5; | |
| 1930 | + if( n==0 && (diffFlags & DIFF_CONTEXT_EX)==0 ) n = 5; | |
| 1767 | 1931 | return n; |
| 1768 | 1932 | } |
| 1769 | 1933 | |
| 1770 | 1934 | /* |
| 1771 | 1935 | ** Extract the width of columns for side-by-side diff. Supply an |
| @@ -1793,22 +1957,21 @@ | ||
| 1793 | 1957 | */ |
| 1794 | 1958 | int *text_diff( |
| 1795 | 1959 | Blob *pA_Blob, /* FROM file */ |
| 1796 | 1960 | Blob *pB_Blob, /* TO file */ |
| 1797 | 1961 | Blob *pOut, /* Write diff here if not NULL */ |
| 1962 | + ReCompiled *pRe, /* Only output changes where this Regexp matches */ | |
| 1798 | 1963 | u64 diffFlags /* DIFF_* flags defined above */ |
| 1799 | 1964 | ){ |
| 1800 | 1965 | int ignoreEolWs; /* Ignore whitespace at the end of lines */ |
| 1801 | - int nContext; /* Amount of context to display */ | |
| 1802 | 1966 | DContext c; |
| 1803 | 1967 | |
| 1804 | 1968 | if( diffFlags & DIFF_INVERT ){ |
| 1805 | 1969 | Blob *pTemp = pA_Blob; |
| 1806 | 1970 | pA_Blob = pB_Blob; |
| 1807 | 1971 | pB_Blob = pTemp; |
| 1808 | 1972 | } |
| 1809 | - nContext = diff_context_lines(diffFlags); | |
| 1810 | 1973 | ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0; |
| 1811 | 1974 | |
| 1812 | 1975 | /* Prepare the input files */ |
| 1813 | 1976 | memset(&c, 0, sizeof(c)); |
| 1814 | 1977 | c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), |
| @@ -1828,17 +1991,14 @@ | ||
| 1828 | 1991 | diff_all(&c); |
| 1829 | 1992 | if( (diffFlags & DIFF_NOOPT)==0 ) diff_optimize(&c); |
| 1830 | 1993 | |
| 1831 | 1994 | if( pOut ){ |
| 1832 | 1995 | /* Compute a context or side-by-side diff into pOut */ |
| 1833 | - int escHtml = (diffFlags & DIFF_HTML)!=0; | |
| 1834 | 1996 | if( diffFlags & DIFF_SIDEBYSIDE ){ |
| 1835 | - int width = diff_width(diffFlags); | |
| 1836 | - sbsDiff(&c, pOut, nContext, width, escHtml); | |
| 1997 | + sbsDiff(&c, pOut, pRe, diffFlags); | |
| 1837 | 1998 | }else{ |
| 1838 | - int showLn = (diffFlags & DIFF_LINENO)!=0; | |
| 1839 | - contextDiff(&c, pOut, nContext, showLn, escHtml); | |
| 1999 | + contextDiff(&c, pOut, pRe, diffFlags); | |
| 1840 | 2000 | } |
| 1841 | 2001 | fossil_free(c.aFrom); |
| 1842 | 2002 | fossil_free(c.aTo); |
| 1843 | 2003 | fossil_free(c.aEdit); |
| 1844 | 2004 | return 0; |
| @@ -1864,19 +2024,19 @@ | ||
| 1864 | 2024 | ** --noopt Disable optimization DIFF_NOOPT |
| 1865 | 2025 | ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE |
| 1866 | 2026 | ** --unified Unified diff. ~DIFF_SIDEBYSIDE |
| 1867 | 2027 | ** --width|-W N N character lines. DIFF_WIDTH_MASK |
| 1868 | 2028 | */ |
| 1869 | -int diff_options(void){ | |
| 2029 | +u64 diff_options(void){ | |
| 1870 | 2030 | u64 diffFlags = 0; |
| 1871 | 2031 | const char *z; |
| 1872 | 2032 | int f; |
| 1873 | 2033 | if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE; |
| 1874 | 2034 | 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 ){ | |
| 1876 | 2036 | if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK; |
| 1877 | - diffFlags |= f; | |
| 2037 | + diffFlags |= f + DIFF_CONTEXT_EX; | |
| 1878 | 2038 | } |
| 1879 | 2039 | if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){ |
| 1880 | 2040 | f *= DIFF_CONTEXT_MASK+1; |
| 1881 | 2041 | if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK; |
| 1882 | 2042 | diffFlags |= f; |
| @@ -1901,34 +2061,52 @@ | ||
| 1901 | 2061 | if( g.argc<4 ) usage("FILE1 FILE2 ..."); |
| 1902 | 2062 | blob_read_from_file(&a, g.argv[2]); |
| 1903 | 2063 | for(i=3; i<g.argc; i++){ |
| 1904 | 2064 | if( i>3 ) fossil_print("-------------------------------\n"); |
| 1905 | 2065 | 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); | |
| 1907 | 2067 | for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ |
| 1908 | 2068 | fossil_print(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]); |
| 1909 | 2069 | } |
| 1910 | 2070 | /* free(R); */ |
| 1911 | 2071 | blob_reset(&b); |
| 1912 | 2072 | } |
| 1913 | 2073 | } |
| 1914 | 2074 | |
| 1915 | 2075 | /* |
| 1916 | -** COMMAND: test-udiff | |
| 2076 | +** COMMAND: test-diff | |
| 2077 | +** | |
| 2078 | +** Usage: %fossil [options] FILE1 FILE2 | |
| 1917 | 2079 | ** |
| 1918 | 2080 | ** Print the difference between two files. The usual diff options apply. |
| 1919 | 2081 | */ |
| 1920 | -void test_udiff_cmd(void){ | |
| 2082 | +void test_diff_cmd(void){ | |
| 1921 | 2083 | 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 */ | |
| 1923 | 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(); | |
| 1924 | 2100 | if( g.argc!=4 ) usage("FILE1 FILE2"); |
| 2101 | + diff_print_filenames(g.argv[2], g.argv[3], diffFlag); | |
| 1925 | 2102 | blob_read_from_file(&a, g.argv[2]); |
| 1926 | 2103 | blob_read_from_file(&b, g.argv[3]); |
| 1927 | 2104 | blob_zero(&out); |
| 1928 | - text_diff(&a, &b, &out, diffFlag); | |
| 2105 | + text_diff(&a, &b, &out, pRe, diffFlag); | |
| 1929 | 2106 | blob_write_to_file(&out, "-"); |
| 2107 | + re_free(pRe); | |
| 1930 | 2108 | } |
| 1931 | 2109 | |
| 1932 | 2110 | /************************************************************************** |
| 1933 | 2111 | ** The basic difference engine is above. What follows is the annotation |
| 1934 | 2112 | ** engine. Both are in the same file since they share many components. |
| 1935 | 2113 |
| --- 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, " <\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, " > ", 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, " <\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, " > ", 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 @@ | ||
| 109 | 109 | if( blob_compare(pFile1, &file2) ){ |
| 110 | 110 | fossil_print("CHANGED %s\n", zName); |
| 111 | 111 | } |
| 112 | 112 | }else{ |
| 113 | 113 | blob_zero(&out); |
| 114 | - text_diff(pFile1, &file2, &out, diffFlags); | |
| 114 | + text_diff(pFile1, &file2, &out, 0, diffFlags); | |
| 115 | 115 | if( blob_size(&out) ){ |
| 116 | 116 | diff_print_filenames(zName, zName2, diffFlags); |
| 117 | 117 | fossil_print("%s\n", blob_str(&out)); |
| 118 | 118 | } |
| 119 | 119 | blob_reset(&out); |
| @@ -210,11 +210,11 @@ | ||
| 210 | 210 | if( diffFlags & DIFF_BRIEF ) return; |
| 211 | 211 | if( zDiffCmd==0 ){ |
| 212 | 212 | Blob out; /* Diff output text */ |
| 213 | 213 | |
| 214 | 214 | blob_zero(&out); |
| 215 | - text_diff(pFile1, pFile2, &out, diffFlags); | |
| 215 | + text_diff(pFile1, pFile2, &out, 0, diffFlags); | |
| 216 | 216 | diff_print_filenames(zName, zName, diffFlags); |
| 217 | 217 | fossil_print("%s\n", blob_str(&out)); |
| 218 | 218 | |
| 219 | 219 | /* Release memory resources */ |
| 220 | 220 | blob_reset(&out); |
| @@ -659,12 +659,18 @@ | ||
| 659 | 659 | char *zCmd; |
| 660 | 660 | blob_zero(&script); |
| 661 | 661 | blob_appendf(&script, "set cmd {| \"%/\" %s --html -y -i", |
| 662 | 662 | g.nameOfExe, zSubCmd); |
| 663 | 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 | + } | |
| 664 | 670 | blob_append(&script, " ", 1); |
| 665 | - shell_escape(&script, g.argv[i]); | |
| 671 | + shell_escape(&script, z); | |
| 666 | 672 | } |
| 667 | 673 | blob_appendf(&script, "}\n%s", zDiffScript); |
| 668 | 674 | zTempFile = write_blob_to_temp_file(&script); |
| 669 | 675 | zCmd = mprintf("tclsh \"%s\"", zTempFile); |
| 670 | 676 | fossil_system(zCmd); |
| 671 | 677 |
| --- 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 @@ | ||
| 380 | 380 | g.zPath = mprintf("%s/%s", g.zPath, zName); |
| 381 | 381 | memcpy(zBaseline, zName, i); |
| 382 | 382 | zBaseline[i] = 0; |
| 383 | 383 | zName += i; |
| 384 | 384 | while( zName[0]=='/' ){ zName++; } |
| 385 | - if( !file_is_simple_pathname(zName) ){ | |
| 385 | + if( !file_is_simple_pathname(zName, 1) ){ | |
| 386 | 386 | int n = strlen(zName); |
| 387 | 387 | if( n>0 && zName[n-1]=='/' ){ |
| 388 | 388 | zName = mprintf("%sindex.html", zName); |
| 389 | - if( !file_is_simple_pathname(zName) ){ | |
| 389 | + if( !file_is_simple_pathname(zName, 1) ){ | |
| 390 | 390 | goto doc_not_found; |
| 391 | 391 | } |
| 392 | 392 | }else{ |
| 393 | 393 | goto doc_not_found; |
| 394 | 394 | } |
| 395 | 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) ){ |
| 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 @@ | ||
| 131 | 131 | static char *EncodeHttp(const char *zIn, int n, int encodeSlash){ |
| 132 | 132 | int c; |
| 133 | 133 | int i = 0; |
| 134 | 134 | int count = 0; |
| 135 | 135 | char *zOut; |
| 136 | - int other; | |
| 137 | 136 | # define IsSafeChar(X) \ |
| 138 | 137 | (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ |
| 139 | - || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other) | |
| 138 | + || (X)=='~' || (X)=='-' || (X)=='_' \ | |
| 139 | + || (!encodeSlash && ((X)=='/' || (X)==':'))) | |
| 140 | 140 | |
| 141 | 141 | if( zIn==0 ) return 0; |
| 142 | 142 | if( n<0 ) n = strlen(zIn); |
| 143 | - other = encodeSlash ? 'a' : '/'; | |
| 144 | 143 | while( i<n && (c = zIn[i])!=0 ){ |
| 145 | 144 | if( IsSafeChar(c) || c==' ' ){ |
| 146 | 145 | count++; |
| 147 | 146 | }else{ |
| 148 | 147 | count += 3; |
| 149 | 148 |
| --- 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 @@ | ||
| 487 | 487 | ** * Does not contain any path element named "." or ".." |
| 488 | 488 | ** * Does not contain any of these characters in the path: "\" |
| 489 | 489 | ** * Does not end with "/". |
| 490 | 490 | ** * Does not contain two or more "/" characters in a row. |
| 491 | 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. | |
| 492 | 496 | */ |
| 493 | -int file_is_simple_pathname(const char *z){ | |
| 497 | +int file_is_simple_pathname(const char *z, int bStrictUtf8){ | |
| 494 | 498 | int i; |
| 495 | 499 | char c = z[0]; |
| 500 | + char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00; | |
| 496 | 501 | if( c=='/' || c==0 ) return 0; |
| 497 | 502 | if( c=='.' ){ |
| 498 | 503 | if( z[1]=='/' || z[1]==0 ) return 0; |
| 499 | 504 | if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; |
| 500 | 505 | } |
| 501 | 506 | 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=='\\' ){ | |
| 528 | 530 | return 0; |
| 529 | 531 | } |
| 530 | 532 | if( c=='/' ){ |
| 531 | 533 | if( z[i+1]=='/' ) return 0; |
| 532 | 534 | if( z[i+1]=='.' ){ |
| 533 | 535 |
| --- 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 |
+2
-4
| --- src/http_socket.c | ||
| +++ src/http_socket.c | ||
| @@ -27,14 +27,12 @@ | ||
| 27 | 27 | */ |
| 28 | 28 | |
| 29 | 29 | #include "config.h" |
| 30 | 30 | #include "http_socket.h" |
| 31 | 31 | #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> | |
| 36 | 34 | #else |
| 37 | 35 | # include <netinet/in.h> |
| 38 | 36 | # include <arpa/inet.h> |
| 39 | 37 | # include <sys/socket.h> |
| 40 | 38 | # include <netdb.h> |
| 41 | 39 |
| --- 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 @@ | ||
| 1 | 1 | /* |
| 2 | 2 | ** Copyright (c) 2009 D. Richard Hipp |
| 3 | 3 | ** |
| 4 | 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. | |
| 5 | +** modify it under the terms of the Simplified BSD License (also | |
| 6 | +** known as the "2-Clause License" or "FreeBSD License".) | |
| 7 | 7 | ** |
| 8 | 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. | |
| 9 | +** but without any warranty; without even the implied warranty of | |
| 10 | +** merchantability or fitness for a particular purpose. | |
| 17 | 11 | ** |
| 18 | 12 | ** Author contact information: |
| 19 | 13 | ** [email protected] |
| 20 | 14 | ** http://www.hwaci.com/drh/ |
| 21 | 15 | ** |
| 22 | 16 |
| --- 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 @@ | ||
| 208 | 208 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 209 | 209 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 210 | 210 | if( vid ){ |
| 211 | 211 | show_common_info(vid, "checkout:", 1, 1); |
| 212 | 212 | } |
| 213 | + fossil_print("checkins: %d\n", | |
| 214 | + db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/")); | |
| 213 | 215 | }else{ |
| 214 | 216 | int rid; |
| 215 | 217 | rid = name_to_rid(g.argv[2]); |
| 216 | 218 | if( rid==0 ){ |
| 217 | 219 | fossil_panic("no such object: %s\n", g.argv[2]); |
| @@ -287,13 +289,18 @@ | ||
| 287 | 289 | } |
| 288 | 290 | } |
| 289 | 291 | |
| 290 | 292 | |
| 291 | 293 | /* |
| 292 | -** Append the difference between two RIDs to the output | |
| 294 | +** Append the difference between artifacts to the output | |
| 293 | 295 | */ |
| 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 | +){ | |
| 295 | 302 | int fromid; |
| 296 | 303 | int toid; |
| 297 | 304 | Blob from, to, out; |
| 298 | 305 | if( zFrom ){ |
| 299 | 306 | fromid = uuid_to_rid(zFrom, 0); |
| @@ -307,16 +314,16 @@ | ||
| 307 | 314 | }else{ |
| 308 | 315 | blob_zero(&to); |
| 309 | 316 | } |
| 310 | 317 | blob_zero(&out); |
| 311 | 318 | if( diffFlags & DIFF_SIDEBYSIDE ){ |
| 312 | - text_diff(&from, &to, &out, diffFlags | DIFF_HTML); | |
| 319 | + text_diff(&from, &to, &out, pRe, diffFlags | DIFF_HTML); | |
| 313 | 320 | @ <div class="sbsdiff"> |
| 314 | 321 | @ %s(blob_str(&out)) |
| 315 | 322 | @ </div> |
| 316 | 323 | }else{ |
| 317 | - text_diff(&from, &to, &out, diffFlags | DIFF_LINENO | DIFF_HTML); | |
| 324 | + text_diff(&from, &to, &out, pRe, diffFlags | DIFF_LINENO | DIFF_HTML); | |
| 318 | 325 | @ <div class="udiff"> |
| 319 | 326 | @ %s(blob_str(&out)) |
| 320 | 327 | @ </div> |
| 321 | 328 | } |
| 322 | 329 | blob_reset(&from); |
| @@ -333,10 +340,11 @@ | ||
| 333 | 340 | const char *zName, /* Name of the file that has changed */ |
| 334 | 341 | const char *zOld, /* blob.uuid before change. NULL for added files */ |
| 335 | 342 | const char *zNew, /* blob.uuid after change. NULL for deletes */ |
| 336 | 343 | const char *zOldName, /* Prior name. NULL if no name change. */ |
| 337 | 344 | u64 diffFlags, /* Flags for text_diff(). Zero to omit diffs */ |
| 345 | + ReCompiled *pRe, /* Only show diffs that match this regex, if not NULL */ | |
| 338 | 346 | int mperm /* executable or symlink permission for zNew */ |
| 339 | 347 | ){ |
| 340 | 348 | if( !g.perm.Hyperlink ){ |
| 341 | 349 | if( zNew==0 ){ |
| 342 | 350 | @ <p>Deleted %h(zName)</p> |
| @@ -350,21 +358,21 @@ | ||
| 350 | 358 | }else{ |
| 351 | 359 | @ <p>Changes to %h(zName)</p> |
| 352 | 360 | } |
| 353 | 361 | if( diffFlags ){ |
| 354 | 362 | @ <pre style="white-space:pre;"> |
| 355 | - append_diff(zOld, zNew, diffFlags); | |
| 363 | + append_diff(zOld, zNew, diffFlags, pRe); | |
| 356 | 364 | @ </pre> |
| 357 | 365 | } |
| 358 | 366 | }else{ |
| 359 | 367 | if( zOld && zNew ){ |
| 360 | 368 | if( fossil_strcmp(zOld, zNew)!=0 ){ |
| 361 | 369 | @ <p>Modified %z(href("%R/finfo?name=%T",zName))%h(zName)</a> |
| 362 | 370 | @ from %z(href("%R/artifact/%s",zOld))[%S(zOld)]</a> |
| 363 | 371 | @ to %z(href("%R/artifact/%s",zNew))[%S(zNew)].</a> |
| 364 | 372 | }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){ |
| 365 | - @ <p>Name change from | |
| 373 | + @ <p>Name change | |
| 366 | 374 | @ from %z(href("%R/finfo?name=%T",zOldName))%h(zOldName)</a> |
| 367 | 375 | @ to %z(href("%R/finfo?name=%T",zName))%h(zName)</a>. |
| 368 | 376 | }else{ |
| 369 | 377 | @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared") for |
| 370 | 378 | @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a> |
| @@ -376,11 +384,11 @@ | ||
| 376 | 384 | @ <p>Added %z(href("%R/finfo?name=%T",zName))%h(zName)</a> |
| 377 | 385 | @ version %z(href("%R/artifact/%s",zNew))[%S(zNew)]</a> |
| 378 | 386 | } |
| 379 | 387 | if( diffFlags ){ |
| 380 | 388 | @ <pre style="white-space:pre;"> |
| 381 | - append_diff(zOld, zNew, diffFlags); | |
| 389 | + append_diff(zOld, zNew, diffFlags, pRe); | |
| 382 | 390 | @ </pre> |
| 383 | 391 | }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ |
| 384 | 392 | @ |
| 385 | 393 | @ %z(href("%R/fdiff?v1=%S&v2=%S",zOld,zNew))[diff]</a> |
| 386 | 394 | } |
| @@ -444,10 +452,12 @@ | ||
| 444 | 452 | int sideBySide; /* True for side-by-side diffs */ |
| 445 | 453 | u64 diffFlags; /* Flag parameter for text_diff() */ |
| 446 | 454 | const char *zName; /* Name of the checkin to be displayed */ |
| 447 | 455 | const char *zUuid; /* UUID of zName */ |
| 448 | 456 | const char *zParent; /* UUID of the parent checkin (if any) */ |
| 457 | + const char *zRe; /* regex parameter */ | |
| 458 | + ReCompiled *pRe = 0; /* regex */ | |
| 449 | 459 | |
| 450 | 460 | login_check_credentials(); |
| 451 | 461 | if( !g.perm.Read ){ login_needed(); return; } |
| 452 | 462 | zName = P("name"); |
| 453 | 463 | rid = name_to_rid_www("name"); |
| @@ -455,10 +465,12 @@ | ||
| 455 | 465 | style_header("Check-in Information Error"); |
| 456 | 466 | @ No such object: %h(g.argv[2]) |
| 457 | 467 | style_footer(); |
| 458 | 468 | return; |
| 459 | 469 | } |
| 470 | + zRe = P("regex"); | |
| 471 | + if( zRe ) re_compile(&pRe, zRe, 0); | |
| 460 | 472 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 461 | 473 | zParent = db_text(0, |
| 462 | 474 | "SELECT uuid FROM plink, blob" |
| 463 | 475 | " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim", |
| 464 | 476 | rid |
| @@ -692,29 +704,35 @@ | ||
| 692 | 704 | @ show side-by-side diffs</a> |
| 693 | 705 | } |
| 694 | 706 | } |
| 695 | 707 | @ %z(xhref("class='button'","%R/vpatch?from=%S&to=%S",zParent,zUuid)) |
| 696 | 708 | @ patch</a></div> |
| 709 | + if( pRe ){ | |
| 710 | + @ <p><b>Only differences that match regular expression "%h(zRe)" | |
| 711 | + @ are shown.</b></p> | |
| 712 | + } | |
| 697 | 713 | db_prepare(&q, |
| 698 | 714 | "SELECT name," |
| 699 | 715 | " mperm," |
| 700 | 716 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," |
| 701 | 717 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)," |
| 702 | 718 | " (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)" |
| 703 | 719 | " FROM mlink JOIN filename ON filename.fnid=mlink.fnid" |
| 704 | 720 | " WHERE mlink.mid=%d" |
| 721 | + " AND (mlink.fid>0" | |
| 722 | + " OR mlink.fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=%d))" | |
| 705 | 723 | " ORDER BY name /*sort*/", |
| 706 | - rid | |
| 724 | + rid, rid | |
| 707 | 725 | ); |
| 708 | 726 | diffFlags = construct_diff_flags(showDiff, sideBySide); |
| 709 | 727 | while( db_step(&q)==SQLITE_ROW ){ |
| 710 | 728 | const char *zName = db_column_text(&q,0); |
| 711 | 729 | int mperm = db_column_int(&q, 1); |
| 712 | 730 | const char *zOld = db_column_text(&q,2); |
| 713 | 731 | const char *zNew = db_column_text(&q,3); |
| 714 | 732 | 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); | |
| 716 | 734 | } |
| 717 | 735 | db_finalize(&q); |
| 718 | 736 | } |
| 719 | 737 | style_footer(); |
| 720 | 738 | } |
| @@ -933,15 +951,19 @@ | ||
| 933 | 951 | Manifest *pFrom, *pTo; |
| 934 | 952 | ManifestFile *pFileFrom, *pFileTo; |
| 935 | 953 | const char *zBranch; |
| 936 | 954 | const char *zFrom; |
| 937 | 955 | const char *zTo; |
| 956 | + const char *zRe; | |
| 957 | + ReCompiled *pRe = 0; | |
| 938 | 958 | |
| 939 | 959 | login_check_credentials(); |
| 940 | 960 | if( !g.perm.Read ){ login_needed(); return; } |
| 941 | 961 | login_anonymous_available(); |
| 942 | 962 | |
| 963 | + zRe = P("regex"); | |
| 964 | + if( zRe ) re_compile(&pRe, zRe, 0); | |
| 943 | 965 | zBranch = P("branch"); |
| 944 | 966 | if( zBranch && zBranch[0] ){ |
| 945 | 967 | cgi_replace_parameter("from", mprintf("root:%s", zBranch)); |
| 946 | 968 | cgi_replace_parameter("to", zBranch); |
| 947 | 969 | } |
| @@ -969,11 +991,16 @@ | ||
| 969 | 991 | style_header("Check-in Differences"); |
| 970 | 992 | @ <h2>Difference From:</h2><blockquote> |
| 971 | 993 | checkin_description(ridFrom); |
| 972 | 994 | @ </blockquote><h2>To:</h2><blockquote> |
| 973 | 995 | 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> | |
| 975 | 1002 | |
| 976 | 1003 | manifest_file_rewind(pFrom); |
| 977 | 1004 | pFileFrom = manifest_file_next(pFrom, 0); |
| 978 | 1005 | manifest_file_rewind(pTo); |
| 979 | 1006 | pFileTo = manifest_file_next(pTo, 0); |
| @@ -987,25 +1014,25 @@ | ||
| 987 | 1014 | }else{ |
| 988 | 1015 | cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); |
| 989 | 1016 | } |
| 990 | 1017 | if( cmp<0 ){ |
| 991 | 1018 | append_file_change_line(pFileFrom->zName, |
| 992 | - pFileFrom->zUuid, 0, 0, diffFlags, 0); | |
| 1019 | + pFileFrom->zUuid, 0, 0, diffFlags, pRe, 0); | |
| 993 | 1020 | pFileFrom = manifest_file_next(pFrom, 0); |
| 994 | 1021 | }else if( cmp>0 ){ |
| 995 | 1022 | append_file_change_line(pFileTo->zName, |
| 996 | - 0, pFileTo->zUuid, 0, diffFlags, | |
| 1023 | + 0, pFileTo->zUuid, 0, diffFlags, pRe, | |
| 997 | 1024 | manifest_file_mperm(pFileTo)); |
| 998 | 1025 | pFileTo = manifest_file_next(pTo, 0); |
| 999 | 1026 | }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ |
| 1000 | 1027 | /* No changes */ |
| 1001 | 1028 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1002 | 1029 | pFileTo = manifest_file_next(pTo, 0); |
| 1003 | 1030 | }else{ |
| 1004 | 1031 | append_file_change_line(pFileFrom->zName, |
| 1005 | 1032 | pFileFrom->zUuid, |
| 1006 | - pFileTo->zUuid, 0, diffFlags, | |
| 1033 | + pFileTo->zUuid, 0, diffFlags, pRe, | |
| 1007 | 1034 | manifest_file_mperm(pFileTo)); |
| 1008 | 1035 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1009 | 1036 | pFileTo = manifest_file_next(pTo, 0); |
| 1010 | 1037 | } |
| 1011 | 1038 | } |
| @@ -1254,11 +1281,11 @@ | ||
| 1254 | 1281 | } |
| 1255 | 1282 | |
| 1256 | 1283 | |
| 1257 | 1284 | /* |
| 1258 | 1285 | ** WEBPAGE: fdiff |
| 1259 | -** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN | |
| 1286 | +** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN®ex=REGEX | |
| 1260 | 1287 | ** |
| 1261 | 1288 | ** Two arguments, v1 and v2, identify the files to be diffed. Show the |
| 1262 | 1289 | ** difference between the two artifacts. Show diff side by side unless sbs |
| 1263 | 1290 | ** is 0. Generate plaintext if "patch" is present. |
| 1264 | 1291 | */ |
| @@ -1267,10 +1294,12 @@ | ||
| 1267 | 1294 | int isPatch; |
| 1268 | 1295 | int sideBySide; |
| 1269 | 1296 | Blob c1, c2, diff, *pOut; |
| 1270 | 1297 | char *zV1; |
| 1271 | 1298 | char *zV2; |
| 1299 | + const char *zRe; | |
| 1300 | + ReCompiled *pRe = 0; | |
| 1272 | 1301 | u64 diffFlags; |
| 1273 | 1302 | const char *zStyle = "sbsdiff"; |
| 1274 | 1303 | |
| 1275 | 1304 | login_check_credentials(); |
| 1276 | 1305 | if( !g.perm.Read ){ login_needed(); return; } |
| @@ -1294,13 +1323,15 @@ | ||
| 1294 | 1323 | }else{ |
| 1295 | 1324 | diffFlags |= DIFF_LINENO; |
| 1296 | 1325 | zStyle = "udiff"; |
| 1297 | 1326 | } |
| 1298 | 1327 | } |
| 1328 | + zRe = P("regex"); | |
| 1329 | + if( zRe ) re_compile(&pRe, zRe, 0); | |
| 1299 | 1330 | content_get(v1, &c1); |
| 1300 | 1331 | content_get(v2, &c2); |
| 1301 | - text_diff(&c1, &c2, pOut, diffFlags); | |
| 1332 | + text_diff(&c1, &c2, pOut, pRe, diffFlags); | |
| 1302 | 1333 | blob_reset(&c1); |
| 1303 | 1334 | blob_reset(&c2); |
| 1304 | 1335 | if( !isPatch ){ |
| 1305 | 1336 | style_header("Diff"); |
| 1306 | 1337 | style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch", |
| @@ -1324,10 +1355,14 @@ | ||
| 1324 | 1355 | @ Artifact %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a>:</h2> |
| 1325 | 1356 | object_description(v1, 0, 0); |
| 1326 | 1357 | @ <h2>To Artifact %z(href("%R/artifact/%S",zV2))[%S(zV2)]</a>:</h2> |
| 1327 | 1358 | object_description(v2, 0, 0); |
| 1328 | 1359 | } |
| 1360 | + if( pRe ){ | |
| 1361 | + @ <b>Only differences that match regular expression "%h(zRe)" | |
| 1362 | + @ are shown.</b> | |
| 1363 | + } | |
| 1329 | 1364 | @ <hr /> |
| 1330 | 1365 | @ <div class="%s(zStyle)"> |
| 1331 | 1366 | @ %s(blob_str(&diff)) |
| 1332 | 1367 | @ </div> |
| 1333 | 1368 | blob_reset(&diff); |
| 1334 | 1369 |
| --- 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 | @ |
| 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 side-by-side 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 | @ |
| 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 side-by-side 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®ex=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 @@ | ||
| 2085 | 2085 | cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n)); |
| 2086 | 2086 | }/*full*/ |
| 2087 | 2087 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 2088 | 2088 | " + 0.99"); |
| 2089 | 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)); | |
| 2090 | + cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425)); | |
| 2091 | 2091 | sqlite3_snprintf(BufLen, zBuf, db_get("project-code","")); |
| 2092 | 2092 | SETBUF(jo, "projectCode"); |
| 2093 | - sqlite3_snprintf(BufLen, zBuf, db_get("server-code","")); | |
| 2094 | - SETBUF(jo, "serverCode"); | |
| 2095 | 2093 | cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME))); |
| 2096 | 2094 | |
| 2097 | 2095 | jv2 = cson_value_new_object(); |
| 2098 | 2096 | jo2 = cson_value_get_object(jv2); |
| 2099 | 2097 | cson_object_set(jo, "sqlite", jv2); |
| 2100 | 2098 |
| --- 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 @@ | ||
| 58 | 58 | return NULL; |
| 59 | 59 | } |
| 60 | 60 | content_get(fromid, &from); |
| 61 | 61 | content_get(toid, &to); |
| 62 | 62 | blob_zero(&out); |
| 63 | - text_diff(&from, &to, &out, flags); | |
| 63 | + text_diff(&from, &to, &out, 0, flags); | |
| 64 | 64 | blob_reset(&from); |
| 65 | 65 | blob_reset(&to); |
| 66 | 66 | outLen = blob_size(&out); |
| 67 | 67 | if(outLen>=0){ |
| 68 | 68 | rc = cson_value_new_string(blob_buffer(&out), |
| 69 | 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, 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 |
+1
-1
| --- src/json_timeline.c | ||
| +++ src/json_timeline.c | ||
| @@ -90,11 +90,11 @@ | ||
| 90 | 90 | |
| 91 | 91 | /* |
| 92 | 92 | ** Return a pointer to a constant string that forms the basis |
| 93 | 93 | ** for a timeline query for the JSON interface. |
| 94 | 94 | */ |
| 95 | -const char const * json_timeline_query(void){ | |
| 95 | +char const * json_timeline_query(void){ | |
| 96 | 96 | /* Field order MUST match that from json_timeline_temp_table()!!! */ |
| 97 | 97 | static const char zBaseSql[] = |
| 98 | 98 | @ SELECT |
| 99 | 99 | @ NULL, |
| 100 | 100 | @ blob.rid, |
| 101 | 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 | 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 @@ | ||
| 543 | 543 | blob_init(&w1, pW1->zWiki, -1); |
| 544 | 544 | blob_zero(&w2); |
| 545 | 545 | blob_init(&w2, pW2->zWiki, -1); |
| 546 | 546 | blob_zero(&d); |
| 547 | 547 | diffFlags = DIFF_IGNORE_EOLWS | DIFF_INLINE; |
| 548 | - text_diff(&w2, &w1, &d, diffFlags); | |
| 548 | + text_diff(&w2, &w1, &d, 0, diffFlags); | |
| 549 | 549 | blob_reset(&w1); |
| 550 | 550 | blob_reset(&w2); |
| 551 | 551 | |
| 552 | 552 | pay = cson_new_object(); |
| 553 | 553 | |
| 554 | 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, 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 @@ | ||
| 397 | 397 | } |
| 398 | 398 | if( memcmp(zAgent, "Mozilla/", 8)==0 ){ |
| 399 | 399 | if( atoi(&zAgent[8])<4 ) return 0; /* Many bots advertise as Mozilla/3 */ |
| 400 | 400 | if( strglob("*Firefox/[1-9]*", zAgent) ) return 1; |
| 401 | 401 | 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; | |
| 403 | 403 | if( strglob("*AppleWebKit/[1-9]*(KHTML*", zAgent) ) return 1; |
| 404 | 404 | return 0; |
| 405 | 405 | } |
| 406 | 406 | if( memcmp(zAgent, "Opera/", 6)==0 ) return 1; |
| 407 | 407 | if( memcmp(zAgent, "Safari/", 7)==0 ) return 1; |
| @@ -625,11 +625,11 @@ | ||
| 625 | 625 | @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>. |
| 626 | 626 | } |
| 627 | 627 | if( zAnonPw ){ |
| 628 | 628 | unsigned int uSeed = captcha_seed(); |
| 629 | 629 | char const *zDecoded = captcha_decode(uSeed); |
| 630 | - int bAutoCaptcha = db_get_boolean("auto-captcha", 1); | |
| 630 | + int bAutoCaptcha = db_get_boolean("auto-captcha", 0); | |
| 631 | 631 | char *zCaptcha = captcha_render(zDecoded); |
| 632 | 632 | |
| 633 | 633 | @ <p><input type="hidden" name="cs" value="%u(uSeed)" /> |
| 634 | 634 | @ Visitors may enter <b>anonymous</b> as the user-ID with |
| 635 | 635 | @ the 8-character hexadecimal password shown below:</p> |
| 636 | 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?[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 @@ | ||
| 605 | 605 | if( g.cgiOutput && once ){ |
| 606 | 606 | once = 0; |
| 607 | 607 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 608 | 608 | cgi_reply(); |
| 609 | 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); | |
| 610 | + fossil_trace("%s: %s\n", g.argv[0], z); | |
| 614 | 611 | } |
| 615 | 612 | } |
| 616 | 613 | free(z); |
| 617 | 614 | db_force_rollback(); |
| 618 | 615 | fossil_exit(rc); |
| @@ -636,17 +633,14 @@ | ||
| 636 | 633 | else |
| 637 | 634 | #endif |
| 638 | 635 | { |
| 639 | 636 | if( g.cgiOutput ){ |
| 640 | 637 | g.cgiOutput = 0; |
| 641 | - cgi_printf("<p class=\"generalError\">%h</p>", z); | |
| 638 | + cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); | |
| 642 | 639 | cgi_reply(); |
| 643 | 640 | }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); | |
| 648 | 642 | } |
| 649 | 643 | } |
| 650 | 644 | free(z); |
| 651 | 645 | db_force_rollback(); |
| 652 | 646 | fossil_exit(rc); |
| @@ -679,17 +673,14 @@ | ||
| 679 | 673 | } else |
| 680 | 674 | #endif |
| 681 | 675 | { |
| 682 | 676 | if( g.cgiOutput ){ |
| 683 | 677 | g.cgiOutput = 0; |
| 684 | - cgi_printf("<p class=\"generalError\">%h</p>", z); | |
| 678 | + cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); | |
| 685 | 679 | cgi_reply(); |
| 686 | 680 | }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); | |
| 691 | 682 | } |
| 692 | 683 | } |
| 693 | 684 | db_force_rollback(); |
| 694 | 685 | fossil_exit(rc); |
| 695 | 686 | } |
| @@ -707,16 +698,13 @@ | ||
| 707 | 698 | json_warn( FSL_JSON_W_UNKNOWN, z ); |
| 708 | 699 | }else |
| 709 | 700 | #endif |
| 710 | 701 | { |
| 711 | 702 | if( g.cgiOutput ){ |
| 712 | - cgi_printf("<p class=\"generalError\">%h</p>", z); | |
| 703 | + cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); | |
| 713 | 704 | }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); | |
| 718 | 706 | } |
| 719 | 707 | } |
| 720 | 708 | free(z); |
| 721 | 709 | } |
| 722 | 710 | |
| @@ -747,13 +735,11 @@ | ||
| 747 | 735 | ** Who knows why - this is just the way windows works. |
| 748 | 736 | */ |
| 749 | 737 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 750 | 738 | WCHAR *zUnicode = fossil_utf8_to_unicode(zNewCmd); |
| 751 | 739 | 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); | |
| 755 | 741 | } |
| 756 | 742 | rc = _wsystem(zUnicode); |
| 757 | 743 | fossil_unicode_free(zUnicode); |
| 758 | 744 | free(zNewCmd); |
| 759 | 745 | #else |
| @@ -892,11 +878,13 @@ | ||
| 892 | 878 | */ |
| 893 | 879 | void verify_all_options(void){ |
| 894 | 880 | int i; |
| 895 | 881 | for(i=1; i<g.argc; i++){ |
| 896 | 882 | 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]); | |
| 898 | 886 | } |
| 899 | 887 | } |
| 900 | 888 | } |
| 901 | 889 | |
| 902 | 890 | /* |
| @@ -1223,15 +1211,17 @@ | ||
| 1223 | 1211 | } |
| 1224 | 1212 | zRepo = "/"; |
| 1225 | 1213 | }else{ |
| 1226 | 1214 | for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){} |
| 1227 | 1215 | 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] = '/'; | |
| 1231 | 1222 | } |
| 1232 | - zDir[i] = '/'; | |
| 1233 | 1223 | zRepo = &zDir[i]; |
| 1234 | 1224 | } |
| 1235 | 1225 | if( stat(zRepo, &sStat)!=0 ){ |
| 1236 | 1226 | fossil_fatal("cannot stat() repository: %s", zRepo); |
| 1237 | 1227 | } |
| 1238 | 1228 |
| --- 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 @@ | ||
| 81 | 81 | $(SRCDIR)/pivot.c \ |
| 82 | 82 | $(SRCDIR)/popen.c \ |
| 83 | 83 | $(SRCDIR)/pqueue.c \ |
| 84 | 84 | $(SRCDIR)/printf.c \ |
| 85 | 85 | $(SRCDIR)/rebuild.c \ |
| 86 | + $(SRCDIR)/regexp.c \ | |
| 86 | 87 | $(SRCDIR)/report.c \ |
| 87 | 88 | $(SRCDIR)/rss.c \ |
| 88 | 89 | $(SRCDIR)/schema.c \ |
| 89 | 90 | $(SRCDIR)/search.c \ |
| 90 | 91 | $(SRCDIR)/setup.c \ |
| @@ -101,10 +102,11 @@ | ||
| 101 | 102 | $(SRCDIR)/th_main.c \ |
| 102 | 103 | $(SRCDIR)/timeline.c \ |
| 103 | 104 | $(SRCDIR)/tkt.c \ |
| 104 | 105 | $(SRCDIR)/tktsetup.c \ |
| 105 | 106 | $(SRCDIR)/undo.c \ |
| 107 | + $(SRCDIR)/unicode.c \ | |
| 106 | 108 | $(SRCDIR)/update.c \ |
| 107 | 109 | $(SRCDIR)/url.c \ |
| 108 | 110 | $(SRCDIR)/user.c \ |
| 109 | 111 | $(SRCDIR)/utf8.c \ |
| 110 | 112 | $(SRCDIR)/verify.c \ |
| @@ -185,10 +187,11 @@ | ||
| 185 | 187 | $(OBJDIR)/pivot_.c \ |
| 186 | 188 | $(OBJDIR)/popen_.c \ |
| 187 | 189 | $(OBJDIR)/pqueue_.c \ |
| 188 | 190 | $(OBJDIR)/printf_.c \ |
| 189 | 191 | $(OBJDIR)/rebuild_.c \ |
| 192 | + $(OBJDIR)/regexp_.c \ | |
| 190 | 193 | $(OBJDIR)/report_.c \ |
| 191 | 194 | $(OBJDIR)/rss_.c \ |
| 192 | 195 | $(OBJDIR)/schema_.c \ |
| 193 | 196 | $(OBJDIR)/search_.c \ |
| 194 | 197 | $(OBJDIR)/setup_.c \ |
| @@ -205,10 +208,11 @@ | ||
| 205 | 208 | $(OBJDIR)/th_main_.c \ |
| 206 | 209 | $(OBJDIR)/timeline_.c \ |
| 207 | 210 | $(OBJDIR)/tkt_.c \ |
| 208 | 211 | $(OBJDIR)/tktsetup_.c \ |
| 209 | 212 | $(OBJDIR)/undo_.c \ |
| 213 | + $(OBJDIR)/unicode_.c \ | |
| 210 | 214 | $(OBJDIR)/update_.c \ |
| 211 | 215 | $(OBJDIR)/url_.c \ |
| 212 | 216 | $(OBJDIR)/user_.c \ |
| 213 | 217 | $(OBJDIR)/utf8_.c \ |
| 214 | 218 | $(OBJDIR)/verify_.c \ |
| @@ -289,10 +293,11 @@ | ||
| 289 | 293 | $(OBJDIR)/pivot.o \ |
| 290 | 294 | $(OBJDIR)/popen.o \ |
| 291 | 295 | $(OBJDIR)/pqueue.o \ |
| 292 | 296 | $(OBJDIR)/printf.o \ |
| 293 | 297 | $(OBJDIR)/rebuild.o \ |
| 298 | + $(OBJDIR)/regexp.o \ | |
| 294 | 299 | $(OBJDIR)/report.o \ |
| 295 | 300 | $(OBJDIR)/rss.o \ |
| 296 | 301 | $(OBJDIR)/schema.o \ |
| 297 | 302 | $(OBJDIR)/search.o \ |
| 298 | 303 | $(OBJDIR)/setup.o \ |
| @@ -309,10 +314,11 @@ | ||
| 309 | 314 | $(OBJDIR)/th_main.o \ |
| 310 | 315 | $(OBJDIR)/timeline.o \ |
| 311 | 316 | $(OBJDIR)/tkt.o \ |
| 312 | 317 | $(OBJDIR)/tktsetup.o \ |
| 313 | 318 | $(OBJDIR)/undo.o \ |
| 319 | + $(OBJDIR)/unicode.o \ | |
| 314 | 320 | $(OBJDIR)/update.o \ |
| 315 | 321 | $(OBJDIR)/url.o \ |
| 316 | 322 | $(OBJDIR)/user.o \ |
| 317 | 323 | $(OBJDIR)/utf8.o \ |
| 318 | 324 | $(OBJDIR)/verify.o \ |
| @@ -390,11 +396,11 @@ | ||
| 390 | 396 | |
| 391 | 397 | |
| 392 | 398 | $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex |
| 393 | 399 | $(OBJDIR)/mkindex $(TRANS_SRC) >$@ |
| 394 | 400 | $(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 | |
| 396 | 402 | touch $(OBJDIR)/headers |
| 397 | 403 | $(OBJDIR)/headers: Makefile |
| 398 | 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 |
| 399 | 405 | Makefile: |
| 400 | 406 | $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate |
| @@ -878,10 +884,17 @@ | ||
| 878 | 884 | |
| 879 | 885 | $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h |
| 880 | 886 | $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c |
| 881 | 887 | |
| 882 | 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 | |
| 883 | 896 | $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate |
| 884 | 897 | $(OBJDIR)/translate $(SRCDIR)/report.c >$(OBJDIR)/report_.c |
| 885 | 898 | |
| 886 | 899 | $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h |
| 887 | 900 | $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c |
| @@ -1018,10 +1031,17 @@ | ||
| 1018 | 1031 | |
| 1019 | 1032 | $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h |
| 1020 | 1033 | $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c |
| 1021 | 1034 | |
| 1022 | 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 | |
| 1023 | 1043 | $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate |
| 1024 | 1044 | $(OBJDIR)/translate $(SRCDIR)/update.c >$(OBJDIR)/update_.c |
| 1025 | 1045 | |
| 1026 | 1046 | $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h |
| 1027 | 1047 | $(XTCC) -o $(OBJDIR)/update.o -c $(OBJDIR)/update_.c |
| 1028 | 1048 |
| --- 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 |
+2
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -84,10 +84,11 @@ | ||
| 84 | 84 | pivot |
| 85 | 85 | popen |
| 86 | 86 | pqueue |
| 87 | 87 | printf |
| 88 | 88 | rebuild |
| 89 | + regexp | |
| 89 | 90 | report |
| 90 | 91 | rss |
| 91 | 92 | schema |
| 92 | 93 | search |
| 93 | 94 | setup |
| @@ -104,10 +105,11 @@ | ||
| 104 | 105 | th_main |
| 105 | 106 | timeline |
| 106 | 107 | tkt |
| 107 | 108 | tktsetup |
| 108 | 109 | undo |
| 110 | + unicode | |
| 109 | 111 | update |
| 110 | 112 | url |
| 111 | 113 | user |
| 112 | 114 | utf8 |
| 113 | 115 | verify |
| 114 | 116 |
| --- 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 @@ | ||
| 430 | 430 | zTarget = next_token(&x, &nTarget); |
| 431 | 431 | zSrc = next_token(&x, &nSrc); |
| 432 | 432 | if( zName==0 || zTarget==0 ) goto manifest_syntax_error; |
| 433 | 433 | if( p->zAttachName!=0 ) goto manifest_syntax_error; |
| 434 | 434 | defossilize(zName); |
| 435 | - if( !file_is_simple_pathname(zName) ){ | |
| 435 | + if( !file_is_simple_pathname(zName, 0) ){ | |
| 436 | 436 | SYNTAX("invalid filename on A-card"); |
| 437 | 437 | } |
| 438 | 438 | defossilize(zTarget); |
| 439 | 439 | if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE)) |
| 440 | 440 | && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
| @@ -524,11 +524,11 @@ | ||
| 524 | 524 | case 'F': { |
| 525 | 525 | char *zName, *zPerm, *zPriorName; |
| 526 | 526 | zName = next_token(&x,0); |
| 527 | 527 | if( zName==0 ) SYNTAX("missing filename on F-card"); |
| 528 | 528 | defossilize(zName); |
| 529 | - if( !file_is_simple_pathname(zName) ){ | |
| 529 | + if( !file_is_simple_pathname(zName, 0) ){ | |
| 530 | 530 | SYNTAX("F-card filename is not a simple path"); |
| 531 | 531 | } |
| 532 | 532 | zUuid = next_token(&x, &sz); |
| 533 | 533 | if( p->zBaseline==0 || zUuid!=0 ){ |
| 534 | 534 | if( sz!=UUID_SIZE ) SYNTAX("F-card UUID is the wrong size"); |
| @@ -536,11 +536,11 @@ | ||
| 536 | 536 | } |
| 537 | 537 | zPerm = next_token(&x,0); |
| 538 | 538 | zPriorName = next_token(&x,0); |
| 539 | 539 | if( zPriorName ){ |
| 540 | 540 | defossilize(zPriorName); |
| 541 | - if( !file_is_simple_pathname(zPriorName) ){ | |
| 541 | + if( !file_is_simple_pathname(zPriorName, 0) ){ | |
| 542 | 542 | SYNTAX("F-card old filename is not a simple path"); |
| 543 | 543 | } |
| 544 | 544 | } |
| 545 | 545 | if( p->nFile>=p->nFileAlloc ){ |
| 546 | 546 | p->nFileAlloc = p->nFileAlloc*2 + 10; |
| @@ -1983,5 +1983,23 @@ | ||
| 1983 | 1983 | manifest_destroy(p); |
| 1984 | 1984 | } |
| 1985 | 1985 | assert( blob_is_reset(pContent) ); |
| 1986 | 1986 | return 1; |
| 1987 | 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 | +} | |
| 1988 | 2006 |
| --- 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 @@ | ||
| 57 | 57 | |
| 58 | 58 | |
| 59 | 59 | /* |
| 60 | 60 | ** COMMAND: merge |
| 61 | 61 | ** |
| 62 | -** Usage: %fossil merge ?OPTIONS? VERSION | |
| 62 | +** Usage: %fossil merge ?OPTIONS? ?VERSION? | |
| 63 | 63 | ** |
| 64 | 64 | ** The argument VERSION is a version that should be merged into the |
| 65 | 65 | ** current checkout. All changes from VERSION back to the nearest |
| 66 | 66 | ** common ancestor are merged. Except, if either of the --cherrypick or |
| 67 | 67 | ** --backout options are used only the changes associated with the |
| 68 | 68 | ** single check-in VERSION are merged. The --backout option causes |
| 69 | 69 | ** the changes associated with VERSION to be removed from the current |
| 70 | 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. | |
| 71 | 74 | ** |
| 72 | 75 | ** Only file content is merged. The result continues to use the |
| 73 | 76 | ** file and directory names from the current checkout even if those |
| 74 | 77 | ** names might have been changed in the branch being merged in. |
| 75 | 78 | ** |
| @@ -87,11 +90,11 @@ | ||
| 87 | 90 | ** option overrides the "binary-glob" setting. |
| 88 | 91 | ** |
| 89 | 92 | ** --nochange | -n Dryrun: do not actually make any changes; just |
| 90 | 93 | ** show what would have happened. |
| 91 | 94 | ** |
| 92 | -** --case-sensitive BOOL Overwrite the case-sensitive setting. If false, | |
| 95 | +** --case-sensitive BOOL Override the case-sensitive setting. If false, | |
| 93 | 96 | ** files whose names differ only in case are taken |
| 94 | 97 | ** to be the same file. |
| 95 | 98 | ** |
| 96 | 99 | ** --force | -f Force the merge even if it would be a no-op. |
| 97 | 100 | */ |
| @@ -131,24 +134,84 @@ | ||
| 131 | 134 | zBinGlob = find_option("binary",0,1); |
| 132 | 135 | nochangeFlag = find_option("nochange","n",0)!=0; |
| 133 | 136 | forceFlag = find_option("force","f",0)!=0; |
| 134 | 137 | zPivot = find_option("baseline",0,1); |
| 135 | 138 | capture_case_sensitive_option(); |
| 136 | - if( g.argc!=3 ){ | |
| 137 | - usage("VERSION"); | |
| 138 | - } | |
| 139 | + verify_all_options(); | |
| 139 | 140 | db_must_be_within_tree(); |
| 140 | 141 | caseSensitive = filenames_are_case_sensitive(); |
| 141 | 142 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 142 | 143 | vid = db_lget_int("checkout", 0); |
| 143 | 144 | if( vid==0 ){ |
| 144 | 145 | fossil_fatal("nothing is checked out"); |
| 145 | 146 | } |
| 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; | |
| 149 | 211 | } |
| 212 | + | |
| 150 | 213 | if( zPivot ){ |
| 151 | 214 | pid = name_to_typed_rid(zPivot, "ci"); |
| 152 | 215 | if( pid==0 || !is_a_version(pid) ){ |
| 153 | 216 | fossil_fatal("not a version: %s", zPivot); |
| 154 | 217 | } |
| @@ -347,11 +410,11 @@ | ||
| 347 | 410 | "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0" |
| 348 | 411 | ); |
| 349 | 412 | while( db_step(&q)==SQLITE_ROW ){ |
| 350 | 413 | int idm = db_column_int(&q, 0); |
| 351 | 414 | 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); | |
| 353 | 416 | free(zName); |
| 354 | 417 | db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm); |
| 355 | 418 | } |
| 356 | 419 | db_finalize(&q); |
| 357 | 420 | |
| 358 | 421 |
| --- 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 @@ | ||
| 175 | 175 | ** is the number of lines of text to copy directly from the pivot, |
| 176 | 176 | ** the second integer is the number of lines of text to omit from the |
| 177 | 177 | ** pivot, and the third integer is the number of lines of text that are |
| 178 | 178 | ** inserted. The edit array ends with a triple of 0,0,0. |
| 179 | 179 | */ |
| 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); | |
| 182 | 182 | if( aC1==0 || aC2==0 ){ |
| 183 | 183 | free(aC1); |
| 184 | 184 | free(aC2); |
| 185 | 185 | return -1; |
| 186 | 186 | } |
| 187 | 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); |
| 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 @@ | ||
| 871 | 871 | Blob b = empty_blob; |
| 872 | 872 | vxprintf(&b, zFormat, ap); |
| 873 | 873 | fossil_puts(blob_str(&b), 0); |
| 874 | 874 | blob_reset(&b); |
| 875 | 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); | |
| 876 | 891 | } |
| 877 | 892 | |
| 878 | 893 | /* |
| 879 | 894 | ** Like strcmp() except that it accepts NULL pointers. NULL sorts before |
| 880 | 895 | ** all non-NULL string pointers. Also, this strcmp() is a binary comparison |
| 881 | 896 |
| --- 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 |
+3
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -614,10 +614,13 @@ | ||
| 614 | 614 | } |
| 615 | 615 | if( activateWal ){ |
| 616 | 616 | db_multi_exec("PRAGMA journal_mode=WAL;"); |
| 617 | 617 | } |
| 618 | 618 | } |
| 619 | + fossil_print("Analyzing the database... "); fflush(stdout); | |
| 620 | + db_multi_exec("analyze"); | |
| 621 | + fossil_print("done\n"); | |
| 619 | 622 | if( showStats ){ |
| 620 | 623 | static struct { int idx; const char *zLabel; } aStat[] = { |
| 621 | 624 | { CFTYPE_ANY, "Artifacts:" }, |
| 622 | 625 | { CFTYPE_MANIFEST, "Manifests:" }, |
| 623 | 626 | { CFTYPE_CLUSTER, "Clusters:" }, |
| 624 | 627 | |
| 625 | 628 | 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 |
+2
| --- src/schema.c | ||
| +++ src/schema.c | ||
| @@ -385,10 +385,11 @@ | ||
| 385 | 385 | @ CREATE TABLE ticket( |
| 386 | 386 | @ -- Do not change any column that begins with tkt_ |
| 387 | 387 | @ tkt_id INTEGER PRIMARY KEY, |
| 388 | 388 | @ tkt_uuid TEXT UNIQUE, |
| 389 | 389 | @ tkt_mtime DATE, |
| 390 | +@ tkt_ctime DATE, | |
| 390 | 391 | @ -- Add as many field as required below this line |
| 391 | 392 | @ type TEXT, |
| 392 | 393 | @ status TEXT, |
| 393 | 394 | @ subsystem TEXT, |
| 394 | 395 | @ priority TEXT, |
| @@ -400,10 +401,11 @@ | ||
| 400 | 401 | @ comment TEXT |
| 401 | 402 | @ ); |
| 402 | 403 | @ CREATE TABLE ticketchng( |
| 403 | 404 | @ -- Do not change any column that begins with tkt_ |
| 404 | 405 | @ tkt_id INTEGER REFERENCES ticket, |
| 406 | +@ tkt_rid INTEGER REFERENCES blob, | |
| 405 | 407 | @ tkt_mtime DATE, |
| 406 | 408 | @ -- Add as many fields as required below this line |
| 407 | 409 | @ login TEXT, |
| 408 | 410 | @ username TEXT, |
| 409 | 411 | @ mimetype TEXT, |
| 410 | 412 |
| --- 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 @@ | ||
| 94 | 94 | "Edit HTML text inserted at the top of every page"); |
| 95 | 95 | setup_menu_entry("Footer", "setup_footer", |
| 96 | 96 | "Edit HTML text inserted at the bottom of every page"); |
| 97 | 97 | setup_menu_entry("Moderation", "setup_modreq", |
| 98 | 98 | "Enable/Disable requiring moderator approval of Wiki and/or Ticket" |
| 99 | - "edits and attachments."); | |
| 99 | + " changes and attachments."); | |
| 100 | 100 | setup_menu_entry("Ad-Unit", "setup_adunit", |
| 101 | 101 | "Edit HTML text for an ad unit inserted after the menu bar"); |
| 102 | 102 | setup_menu_entry("Logo", "setup_logo", |
| 103 | 103 | "Change the logo and background images for the server"); |
| 104 | 104 | setup_menu_entry("Shunned", "shun", |
| @@ -109,10 +109,12 @@ | ||
| 109 | 109 | "A record of login attempts"); |
| 110 | 110 | setup_menu_entry("Stats", "stat", |
| 111 | 111 | "Display repository statistics"); |
| 112 | 112 | setup_menu_entry("SQL", "admin_sql", |
| 113 | 113 | "Enter raw SQL commands"); |
| 114 | + setup_menu_entry("TH1", "admin_th1", | |
| 115 | + "Enter raw TH1 commands"); | |
| 114 | 116 | @ </table> |
| 115 | 117 | |
| 116 | 118 | style_footer(); |
| 117 | 119 | } |
| 118 | 120 | |
| @@ -1759,8 +1761,52 @@ | ||
| 1759 | 1761 | @ </tr> |
| 1760 | 1762 | } |
| 1761 | 1763 | sqlite3_finalize(pStmt); |
| 1762 | 1764 | @ </table> |
| 1763 | 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 | + } | |
| 1764 | 1810 | } |
| 1765 | 1811 | style_footer(); |
| 1766 | 1812 | } |
| 1767 | 1813 |
| --- 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 |
+6
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -1477,10 +1477,16 @@ | ||
| 1477 | 1477 | p->zDbFilename, sqlite3_errmsg(db)); |
| 1478 | 1478 | exit(1); |
| 1479 | 1479 | } |
| 1480 | 1480 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1481 | 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 | + } | |
| 1482 | 1488 | #endif |
| 1483 | 1489 | } |
| 1484 | 1490 | } |
| 1485 | 1491 | |
| 1486 | 1492 | /* |
| 1487 | 1493 |
| --- 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 @@ | ||
| 355 | 355 | @ div.footer a:visited { color: white; } |
| 356 | 356 | @ div.footer a:hover { background-color: white; color: #558195; } |
| 357 | 357 | @ |
| 358 | 358 | @ /* <verbatim> blocks */ |
| 359 | 359 | @ pre.verbatim { |
| 360 | -@ background-color: #f5f5f5; | |
| 361 | -@ padding: 0.5em; | |
| 360 | +@ background-color: #f5f5f5; | |
| 361 | +@ padding: 0.5em; | |
| 362 | +@ white-space: pre-wrap; | |
| 362 | 363 | @ } |
| 363 | 364 | @ |
| 364 | 365 | @ /* The label/value pairs on (for example) the ci page */ |
| 365 | 366 | @ table.label-value th { |
| 366 | 367 | @ vertical-align: top; |
| @@ -595,12 +596,13 @@ | ||
| 595 | 596 | @ color: #555; |
| 596 | 597 | @ } |
| 597 | 598 | @ |
| 598 | 599 | @ /* <verbatim> blocks */ |
| 599 | 600 | @ pre.verbatim { |
| 600 | -@ background-color: #f5f5f5; | |
| 601 | -@ padding: 0.5em; | |
| 601 | +@ background-color: #f5f5f5; | |
| 602 | +@ padding: 0.5em; | |
| 603 | +@ white-space: pre-wrap; | |
| 602 | 604 | @ } |
| 603 | 605 | @ |
| 604 | 606 | @ /* The label/value pairs on (for example) the ci page */ |
| 605 | 607 | @ table.label-value th { |
| 606 | 608 | @ vertical-align: top; |
| @@ -1083,13 +1085,14 @@ | ||
| 1083 | 1085 | @ div.footer a:visited { color: white; } |
| 1084 | 1086 | @ div.footer a:hover { background-color: white; color: #558195; } |
| 1085 | 1087 | @ |
| 1086 | 1088 | @ /* verbatim blocks */ |
| 1087 | 1089 | @ 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 | +@ } | |
| 1091 | 1094 | @ |
| 1092 | 1095 | @ /* The label/value pairs on (for example) the ci page */ |
| 1093 | 1096 | @ table.label-value th { |
| 1094 | 1097 | @ vertical-align: top; |
| 1095 | 1098 | @ text-align: right; |
| @@ -1226,10 +1229,12 @@ | ||
| 1226 | 1229 | @ } |
| 1227 | 1230 | @ set version [getVersion $manifest_version] |
| 1228 | 1231 | @ set tclVersion [getTclVersion] |
| 1229 | 1232 | @ set fossilUrl http://www.fossil-scm.org |
| 1230 | 1233 | @ </th1> |
| 1234 | +@ This page was generated in about | |
| 1235 | +@ <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by | |
| 1231 | 1236 | @ <a href="$fossilUrl/">Fossil</a> |
| 1232 | 1237 | @ version $release_version $tclVersion |
| 1233 | 1238 | @ <a href="$fossilUrl/index.html/info/$version">$manifest_version</a> |
| 1234 | 1239 | @ <a href="$fossilUrl/fossil/timeline?c=$manifest_date&y=ci">$manifest_date</a> |
| 1235 | 1240 | @ </div> |
| 1236 | 1241 |
| --- 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&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&y=ci">$manifest_date</a> |
| 1240 | @ </div> |
| 1241 |
+1
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -118,10 +118,11 @@ | ||
| 118 | 118 | sqlcmd_content, 0, 0); |
| 119 | 119 | sqlite3_create_function(db, "compress", 1, SQLITE_ANY, 0, |
| 120 | 120 | sqlcmd_compress, 0, 0); |
| 121 | 121 | sqlite3_create_function(db, "decompress", 1, SQLITE_ANY, 0, |
| 122 | 122 | sqlcmd_decompress, 0, 0); |
| 123 | + re_add_sql_func(db); | |
| 123 | 124 | g.repositoryOpen = 1; |
| 124 | 125 | g.db = db; |
| 125 | 126 | return SQLITE_OK; |
| 126 | 127 | } |
| 127 | 128 | |
| 128 | 129 |
| --- 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 @@ | ||
| 1 | 1 | /****************************************************************************** |
| 2 | 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 | |
| 3 | +** version 3.7.16. By combining all the individual C code files into this | |
| 4 | 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | 8 | ** translation unit. |
| @@ -671,13 +671,13 @@ | ||
| 671 | 671 | ** |
| 672 | 672 | ** See also: [sqlite3_libversion()], |
| 673 | 673 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 674 | 674 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 675 | 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" | |
| 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 | 679 | |
| 680 | 680 | /* |
| 681 | 681 | ** CAPI3REF: Run-Time Library Version Numbers |
| 682 | 682 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 683 | 683 | ** |
| @@ -2156,11 +2156,11 @@ | ||
| 2156 | 2156 | ** database connection is opened. By default, URI handling is globally |
| 2157 | 2157 | ** disabled. The default value may be changed by compiling with the |
| 2158 | 2158 | ** [SQLITE_USE_URI] symbol defined. |
| 2159 | 2159 | ** |
| 2160 | 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 | |
| 2161 | +** <dd> This option takes a single integer argument which is interpreted as | |
| 2162 | 2162 | ** a boolean in order to enable or disable the use of covering indices for |
| 2163 | 2163 | ** full table scans in the query optimizer. The default setting is determined |
| 2164 | 2164 | ** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on" |
| 2165 | 2165 | ** if that compile-time option is omitted. |
| 2166 | 2166 | ** The ability to disable the use of covering indices for full table scans |
| @@ -8238,10 +8238,15 @@ | ||
| 8238 | 8238 | ** A convenience macro that returns the number of elements in |
| 8239 | 8239 | ** an array. |
| 8240 | 8240 | */ |
| 8241 | 8241 | #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) |
| 8242 | 8242 | |
| 8243 | +/* | |
| 8244 | +** Determine if the argument is a power of two | |
| 8245 | +*/ | |
| 8246 | +#define IsPowerOfTwo(X) (((X)&((X)-1))==0) | |
| 8247 | + | |
| 8243 | 8248 | /* |
| 8244 | 8249 | ** The following value as a destructor means to use sqlite3DbFree(). |
| 8245 | 8250 | ** The sqlite3DbFree() routine requires two parameters instead of the |
| 8246 | 8251 | ** one parameter that destructors normally want. So we have to introduce |
| 8247 | 8252 | ** this magic value that the code knows to handle differently. Any |
| @@ -10042,10 +10047,11 @@ | ||
| 10042 | 10047 | #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ |
| 10043 | 10048 | #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ |
| 10044 | 10049 | #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ |
| 10045 | 10050 | #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ |
| 10046 | 10051 | #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ |
| 10052 | +#define SQLITE_Transitive 0x0200 /* Transitive constraints */ | |
| 10047 | 10053 | #define SQLITE_AllOpts 0xffff /* All optimizations */ |
| 10048 | 10054 | |
| 10049 | 10055 | /* |
| 10050 | 10056 | ** Macros for testing whether or not optimizations are enabled or disabled. |
| 10051 | 10057 | */ |
| @@ -10553,24 +10559,24 @@ | ||
| 10553 | 10559 | ** and the value of Index.onError indicate the which conflict resolution |
| 10554 | 10560 | ** algorithm to employ whenever an attempt is made to insert a non-unique |
| 10555 | 10561 | ** element. |
| 10556 | 10562 | */ |
| 10557 | 10563 | 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 */ | |
| 10572 | 10578 | #ifdef SQLITE_ENABLE_STAT3 |
| 10573 | 10579 | int nSample; /* Number of elements in aSample[] */ |
| 10574 | 10580 | tRowcnt avgEq; /* Average nEq value for key values not in aSample */ |
| 10575 | 10581 | IndexSample *aSample; /* Samples of the left-most key */ |
| 10576 | 10582 | #endif |
| @@ -10840,22 +10846,31 @@ | ||
| 10840 | 10846 | ** name. An expr/name combination can be used in several ways, such |
| 10841 | 10847 | ** as the list of "expr AS ID" fields following a "SELECT" or in the |
| 10842 | 10848 | ** list of "ID = expr" items in an UPDATE. A list of expressions can |
| 10843 | 10849 | ** also be used as the argument to a function, in which case the a.zName |
| 10844 | 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. | |
| 10845 | 10859 | */ |
| 10846 | 10860 | struct ExprList { |
| 10847 | 10861 | int nExpr; /* Number of expressions on the list */ |
| 10848 | 10862 | int iECursor; /* VDBE Cursor associated with this ExprList */ |
| 10849 | 10863 | 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 */ | |
| 10857 | 10872 | } *a; /* Alloc a power of two greater or equal to nExpr */ |
| 10858 | 10873 | }; |
| 10859 | 10874 | |
| 10860 | 10875 | /* |
| 10861 | 10876 | ** An instance of this structure is used by the parser to record both |
| @@ -11171,10 +11186,11 @@ | ||
| 11171 | 11186 | #define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */ |
| 11172 | 11187 | #define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ |
| 11173 | 11188 | #define SF_UseSorter 0x0040 /* Sort using a sorter */ |
| 11174 | 11189 | #define SF_Values 0x0080 /* Synthesized from VALUES clause */ |
| 11175 | 11190 | #define SF_Materialize 0x0100 /* Force materialization of views */ |
| 11191 | +#define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ | |
| 11176 | 11192 | |
| 11177 | 11193 | |
| 11178 | 11194 | /* |
| 11179 | 11195 | ** The results of a select can be distributed in several ways. The |
| 11180 | 11196 | ** "SRT" prefix means "SELECT Result Type". |
| @@ -11883,11 +11899,11 @@ | ||
| 11883 | 11899 | SQLITE_PRIVATE Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, |
| 11884 | 11900 | Token*, int, int); |
| 11885 | 11901 | SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int); |
| 11886 | 11902 | SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*); |
| 11887 | 11903 | SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, |
| 11888 | - Expr*,ExprList*,int,Expr*,Expr*); | |
| 11904 | + Expr*,ExprList*,u16,Expr*,Expr*); | |
| 11889 | 11905 | SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*); |
| 11890 | 11906 | SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*); |
| 11891 | 11907 | SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int); |
| 11892 | 11908 | SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); |
| 11893 | 11909 | #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) |
| @@ -12140,10 +12156,11 @@ | ||
| 12140 | 12156 | SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); |
| 12141 | 12157 | SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); |
| 12142 | 12158 | SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*); |
| 12143 | 12159 | SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int); |
| 12144 | 12160 | SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); |
| 12161 | +SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); | |
| 12145 | 12162 | SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); |
| 12146 | 12163 | SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); |
| 12147 | 12164 | SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); |
| 12148 | 12165 | SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); |
| 12149 | 12166 | SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); |
| @@ -12278,12 +12295,14 @@ | ||
| 12278 | 12295 | #define sqlite3FkOldmask(a,b) 0 |
| 12279 | 12296 | #define sqlite3FkRequired(a,b,c,d) 0 |
| 12280 | 12297 | #endif |
| 12281 | 12298 | #ifndef SQLITE_OMIT_FOREIGN_KEY |
| 12282 | 12299 | SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *, Table*); |
| 12300 | +SQLITE_PRIVATE int sqlite3FkLocateIndex(Parse*,Table*,FKey*,Index**,int**); | |
| 12283 | 12301 | #else |
| 12284 | 12302 | #define sqlite3FkDelete(a,b) |
| 12303 | + #define sqlite3FkLocateIndex(a,b,c,d,e) | |
| 12285 | 12304 | #endif |
| 12286 | 12305 | |
| 12287 | 12306 | |
| 12288 | 12307 | /* |
| 12289 | 12308 | ** Available fault injectors. Should be numbered beginning with 0. |
| @@ -23286,15 +23305,11 @@ | ||
| 23286 | 23305 | { "pwrite64", (sqlite3_syscall_ptr)0, 0 }, |
| 23287 | 23306 | #endif |
| 23288 | 23307 | #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ |
| 23289 | 23308 | aSyscall[13].pCurrent) |
| 23290 | 23309 | |
| 23291 | -#if SQLITE_ENABLE_LOCKING_STYLE | |
| 23292 | 23310 | { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, |
| 23293 | -#else | |
| 23294 | - { "fchmod", (sqlite3_syscall_ptr)0, 0 }, | |
| 23295 | -#endif | |
| 23296 | 23311 | #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) |
| 23297 | 23312 | |
| 23298 | 23313 | #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE |
| 23299 | 23314 | { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 }, |
| 23300 | 23315 | #else |
| @@ -23315,13 +23330,10 @@ | ||
| 23315 | 23330 | #define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) |
| 23316 | 23331 | |
| 23317 | 23332 | { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 }, |
| 23318 | 23333 | #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) |
| 23319 | 23334 | |
| 23320 | - { "umask", (sqlite3_syscall_ptr)umask, 0 }, | |
| 23321 | -#define osUmask ((mode_t(*)(mode_t))aSyscall[21].pCurrent) | |
| 23322 | - | |
| 23323 | 23335 | }; /* End of the overrideable system calls */ |
| 23324 | 23336 | |
| 23325 | 23337 | /* |
| 23326 | 23338 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| 23327 | 23339 | ** "unix" VFSes. Return SQLITE_OK opon successfully updating the |
| @@ -23422,31 +23434,29 @@ | ||
| 23422 | 23434 | ** process that is able to write to the database will also be able to |
| 23423 | 23435 | ** recover the hot journals. |
| 23424 | 23436 | */ |
| 23425 | 23437 | static int robust_open(const char *z, int f, mode_t m){ |
| 23426 | 23438 | 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; | |
| 23435 | 23440 | do{ |
| 23436 | 23441 | #if defined(O_CLOEXEC) |
| 23437 | 23442 | fd = osOpen(z,f|O_CLOEXEC,m2); |
| 23438 | 23443 | #else |
| 23439 | 23444 | fd = osOpen(z,f,m2); |
| 23440 | 23445 | #endif |
| 23441 | 23446 | }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 | + } | |
| 23445 | 23454 | #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); | |
| 23447 | 23456 | #endif |
| 23457 | + } | |
| 23448 | 23458 | return fd; |
| 23449 | 23459 | } |
| 23450 | 23460 | |
| 23451 | 23461 | /* |
| 23452 | 23462 | ** Helper functions to obtain and relinquish the global mutex. The |
| @@ -29868,11 +29878,11 @@ | ||
| 29868 | 29878 | }; |
| 29869 | 29879 | unsigned int i; /* Loop counter */ |
| 29870 | 29880 | |
| 29871 | 29881 | /* Double-check that the aSyscall[] array has been constructed |
| 29872 | 29882 | ** correctly. See ticket [bb3a86e890c8e96ab] */ |
| 29873 | - assert( ArraySize(aSyscall)==22 ); | |
| 29883 | + assert( ArraySize(aSyscall)==21 ); | |
| 29874 | 29884 | |
| 29875 | 29885 | /* Register all VFSes defined in the aVfs[] array */ |
| 29876 | 29886 | for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ |
| 29877 | 29887 | sqlite3_vfs_register(&aVfs[i], i==0); |
| 29878 | 29888 | } |
| @@ -56334,11 +56344,11 @@ | ||
| 56334 | 56344 | sqlite3BtreeLeave(p); |
| 56335 | 56345 | return 0; |
| 56336 | 56346 | } |
| 56337 | 56347 | i = PENDING_BYTE_PAGE(pBt); |
| 56338 | 56348 | 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); | |
| 56340 | 56350 | sCheck.errMsg.useMalloc = 2; |
| 56341 | 56351 | |
| 56342 | 56352 | /* Check the integrity of the freelist |
| 56343 | 56353 | */ |
| 56344 | 56354 | checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), |
| @@ -56869,11 +56879,16 @@ | ||
| 56869 | 56879 | /* |
| 56870 | 56880 | ** Parameter zSrcData points to a buffer containing the data for |
| 56871 | 56881 | ** page iSrcPg from the source database. Copy this data into the |
| 56872 | 56882 | ** destination database. |
| 56873 | 56883 | */ |
| 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 | +){ | |
| 56875 | 56890 | Pager * const pDestPager = sqlite3BtreePager(p->pDest); |
| 56876 | 56891 | const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc); |
| 56877 | 56892 | int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest); |
| 56878 | 56893 | const int nCopy = MIN(nSrcPgsz, nDestPgsz); |
| 56879 | 56894 | const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz; |
| @@ -56942,10 +56957,13 @@ | ||
| 56942 | 56957 | ** cached parse of the page). MemPage.isInit is marked |
| 56943 | 56958 | ** "MUST BE FIRST" for this purpose. |
| 56944 | 56959 | */ |
| 56945 | 56960 | memcpy(zOut, zIn, nCopy); |
| 56946 | 56961 | ((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0; |
| 56962 | + if( iOff==0 && bUpdate==0 ){ | |
| 56963 | + sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc)); | |
| 56964 | + } | |
| 56947 | 56965 | } |
| 56948 | 56966 | sqlite3PagerUnref(pDestPg); |
| 56949 | 56967 | } |
| 56950 | 56968 | |
| 56951 | 56969 | return rc; |
| @@ -57048,11 +57066,11 @@ | ||
| 57048 | 57066 | const Pgno iSrcPg = p->iNext; /* Source page number */ |
| 57049 | 57067 | if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){ |
| 57050 | 57068 | DbPage *pSrcPg; /* Source page object */ |
| 57051 | 57069 | rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg); |
| 57052 | 57070 | if( rc==SQLITE_OK ){ |
| 57053 | - rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg)); | |
| 57071 | + rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0); | |
| 57054 | 57072 | sqlite3PagerUnref(pSrcPg); |
| 57055 | 57073 | } |
| 57056 | 57074 | } |
| 57057 | 57075 | p->iNext++; |
| 57058 | 57076 | } |
| @@ -57296,11 +57314,11 @@ | ||
| 57296 | 57314 | ** the new data into the backup. |
| 57297 | 57315 | */ |
| 57298 | 57316 | int rc; |
| 57299 | 57317 | assert( p->pDestDb ); |
| 57300 | 57318 | sqlite3_mutex_enter(p->pDestDb->mutex); |
| 57301 | - rc = backupOnePage(p, iPage, aData); | |
| 57319 | + rc = backupOnePage(p, iPage, aData, 1); | |
| 57302 | 57320 | sqlite3_mutex_leave(p->pDestDb->mutex); |
| 57303 | 57321 | assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED ); |
| 57304 | 57322 | if( rc!=SQLITE_OK ){ |
| 57305 | 57323 | p->rc = rc; |
| 57306 | 57324 | } |
| @@ -71890,10 +71908,18 @@ | ||
| 71890 | 71908 | p->pReal = pReal; |
| 71891 | 71909 | if( p->iSize>0 ){ |
| 71892 | 71910 | assert(p->iSize<=p->nBuf); |
| 71893 | 71911 | rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0); |
| 71894 | 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 | + } | |
| 71895 | 71921 | } |
| 71896 | 71922 | } |
| 71897 | 71923 | return rc; |
| 71898 | 71924 | } |
| 71899 | 71925 | |
| @@ -72636,10 +72662,39 @@ | ||
| 72636 | 72662 | } |
| 72637 | 72663 | } |
| 72638 | 72664 | return 0; |
| 72639 | 72665 | } |
| 72640 | 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 | +} | |
| 72641 | 72696 | |
| 72642 | 72697 | /* |
| 72643 | 72698 | ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up |
| 72644 | 72699 | ** that name in the set of source tables in pSrcList and make the pExpr |
| 72645 | 72700 | ** expression node refer back to that source column. The following changes |
| @@ -72691,44 +72746,63 @@ | ||
| 72691 | 72746 | |
| 72692 | 72747 | /* Initialize the node to no-match */ |
| 72693 | 72748 | pExpr->iTable = -1; |
| 72694 | 72749 | pExpr->pTab = 0; |
| 72695 | 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 | + } | |
| 72696 | 72765 | |
| 72697 | 72766 | /* Start at the inner-most context and move outward until a match is found */ |
| 72698 | 72767 | while( pNC && cnt==0 ){ |
| 72699 | 72768 | ExprList *pEList; |
| 72700 | 72769 | SrcList *pSrcList = pNC->pSrcList; |
| 72701 | 72770 | |
| 72702 | 72771 | if( pSrcList ){ |
| 72703 | 72772 | for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ |
| 72704 | 72773 | Table *pTab; |
| 72705 | - int iDb; | |
| 72706 | 72774 | Column *pCol; |
| 72707 | 72775 | |
| 72708 | 72776 | pTab = pItem->pTab; |
| 72709 | 72777 | assert( pTab!=0 && pTab->zName!=0 ); |
| 72710 | - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); | |
| 72711 | 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 | + } | |
| 72712 | 72796 | 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; | |
| 72724 | 72801 | } |
| 72725 | 72802 | } |
| 72726 | 72803 | if( 0==(cntTab++) ){ |
| 72727 | - pExpr->iTable = pItem->iCursor; | |
| 72728 | - pExpr->pTab = pTab; | |
| 72729 | - pSchema = pTab->pSchema; | |
| 72730 | 72804 | pMatch = pItem; |
| 72731 | 72805 | } |
| 72732 | 72806 | for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ |
| 72733 | 72807 | if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ |
| 72734 | 72808 | /* If there has been exactly one prior match and this match |
| @@ -72738,21 +72812,23 @@ | ||
| 72738 | 72812 | if( cnt==1 ){ |
| 72739 | 72813 | if( pItem->jointype & JT_NATURAL ) continue; |
| 72740 | 72814 | if( nameInUsingClause(pItem->pUsing, zCol) ) continue; |
| 72741 | 72815 | } |
| 72742 | 72816 | cnt++; |
| 72743 | - pExpr->iTable = pItem->iCursor; | |
| 72744 | - pExpr->pTab = pTab; | |
| 72745 | 72817 | pMatch = pItem; |
| 72746 | - pSchema = pTab->pSchema; | |
| 72747 | 72818 | /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ |
| 72748 | 72819 | pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; |
| 72749 | 72820 | break; |
| 72750 | 72821 | } |
| 72751 | 72822 | } |
| 72752 | 72823 | } |
| 72753 | - } | |
| 72824 | + if( pMatch ){ | |
| 72825 | + pExpr->iTable = pMatch->iCursor; | |
| 72826 | + pExpr->pTab = pMatch->pTab; | |
| 72827 | + pSchema = pExpr->pTab->pSchema; | |
| 72828 | + } | |
| 72829 | + } /* if( pSrcList ) */ | |
| 72754 | 72830 | |
| 72755 | 72831 | #ifndef SQLITE_OMIT_TRIGGER |
| 72756 | 72832 | /* If we have not already resolved the name, then maybe |
| 72757 | 72833 | ** it is a new.* or old.* trigger argument reference |
| 72758 | 72834 | */ |
| @@ -73083,11 +73159,11 @@ | ||
| 73083 | 73159 | #endif |
| 73084 | 73160 | if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){ |
| 73085 | 73161 | sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); |
| 73086 | 73162 | pNC->nErr++; |
| 73087 | 73163 | is_agg = 0; |
| 73088 | - }else if( no_such_func ){ | |
| 73164 | + }else if( no_such_func && pParse->db->init.busy==0 ){ | |
| 73089 | 73165 | sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); |
| 73090 | 73166 | pNC->nErr++; |
| 73091 | 73167 | }else if( wrong_num_args ){ |
| 73092 | 73168 | sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", |
| 73093 | 73169 | nId, zId); |
| @@ -73519,27 +73595,10 @@ | ||
| 73519 | 73595 | if( sqlite3ResolveExprNames(&sNC, p->pLimit) || |
| 73520 | 73596 | sqlite3ResolveExprNames(&sNC, p->pOffset) ){ |
| 73521 | 73597 | return WRC_Abort; |
| 73522 | 73598 | } |
| 73523 | 73599 | |
| 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 | 73600 | /* Recursively resolve names in all subqueries |
| 73542 | 73601 | */ |
| 73543 | 73602 | for(i=0; i<p->pSrc->nSrc; i++){ |
| 73544 | 73603 | struct SrcList_item *pItem = &p->pSrc->a[i]; |
| 73545 | 73604 | if( pItem->pSelect ){ |
| @@ -73562,10 +73621,27 @@ | ||
| 73562 | 73621 | for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef; |
| 73563 | 73622 | assert( pItem->isCorrelated==0 && nRef<=0 ); |
| 73564 | 73623 | pItem->isCorrelated = (nRef!=0); |
| 73565 | 73624 | } |
| 73566 | 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 | + } | |
| 73567 | 73643 | |
| 73568 | 73644 | /* If there are no aggregate functions in the result-set, and no GROUP BY |
| 73569 | 73645 | ** expression, do not allow aggregates in any of the other expressions. |
| 73570 | 73646 | */ |
| 73571 | 73647 | assert( (p->selFlags & SF_Aggregate)==0 ); |
| @@ -77046,10 +77122,16 @@ | ||
| 77046 | 77122 | for(i=0; i<pList->nExpr; i++){ |
| 77047 | 77123 | sqlite3ExplainPrintf(pOut, "item[%d] = ", i); |
| 77048 | 77124 | sqlite3ExplainPush(pOut); |
| 77049 | 77125 | sqlite3ExplainExpr(pOut, pList->a[i].pExpr); |
| 77050 | 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 | + } | |
| 77051 | 77133 | if( i<pList->nExpr-1 ){ |
| 77052 | 77134 | sqlite3ExplainNL(pOut); |
| 77053 | 77135 | } |
| 77054 | 77136 | } |
| 77055 | 77137 | sqlite3ExplainPop(pOut); |
| @@ -87523,11 +87605,11 @@ | ||
| 87523 | 87605 | |
| 87524 | 87606 | /* |
| 87525 | 87607 | ** A foreign key constraint requires that the key columns in the parent |
| 87526 | 87608 | ** table are collectively subject to a UNIQUE or PRIMARY KEY constraint. |
| 87527 | 87609 | ** 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. | |
| 87529 | 87611 | ** |
| 87530 | 87612 | ** If successful, zero is returned. If the parent key is an INTEGER PRIMARY |
| 87531 | 87613 | ** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx |
| 87532 | 87614 | ** is set to point to the unique index. |
| 87533 | 87615 | ** |
| @@ -87559,11 +87641,11 @@ | ||
| 87559 | 87641 | ** |
| 87560 | 87642 | ** then non-zero is returned, and a "foreign key mismatch" error loaded |
| 87561 | 87643 | ** into pParse. If an OOM error occurs, non-zero is returned and the |
| 87562 | 87644 | ** pParse->db->mallocFailed flag is set. |
| 87563 | 87645 | */ |
| 87564 | -static int locateFkeyIndex( | |
| 87646 | +SQLITE_PRIVATE int sqlite3FkLocateIndex( | |
| 87565 | 87647 | Parse *pParse, /* Parse context to store any error in */ |
| 87566 | 87648 | Table *pParent, /* Parent table of FK constraint pFKey */ |
| 87567 | 87649 | FKey *pFKey, /* Foreign key to find index for */ |
| 87568 | 87650 | Index **ppIdx, /* OUT: Unique index on parent table */ |
| 87569 | 87651 | int **paiCol /* OUT: Map of index columns in pFKey */ |
| @@ -87656,11 +87738,13 @@ | ||
| 87656 | 87738 | } |
| 87657 | 87739 | } |
| 87658 | 87740 | |
| 87659 | 87741 | if( !pIdx ){ |
| 87660 | 87742 | 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); | |
| 87662 | 87746 | } |
| 87663 | 87747 | sqlite3DbFree(pParse->db, aiCol); |
| 87664 | 87748 | return 1; |
| 87665 | 87749 | } |
| 87666 | 87750 | |
| @@ -88117,11 +88201,11 @@ | ||
| 88117 | 88201 | if( pParse->disableTriggers ){ |
| 88118 | 88202 | pTo = sqlite3FindTable(db, pFKey->zTo, zDb); |
| 88119 | 88203 | }else{ |
| 88120 | 88204 | pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb); |
| 88121 | 88205 | } |
| 88122 | - if( !pTo || locateFkeyIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){ | |
| 88206 | + if( !pTo || sqlite3FkLocateIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){ | |
| 88123 | 88207 | assert( isIgnoreErrors==0 || (regOld!=0 && regNew==0) ); |
| 88124 | 88208 | if( !isIgnoreErrors || db->mallocFailed ) return; |
| 88125 | 88209 | if( pTo==0 ){ |
| 88126 | 88210 | /* If isIgnoreErrors is true, then a table is being dropped. In this |
| 88127 | 88211 | ** case SQLite runs a "DELETE FROM xxx" on the table being dropped |
| @@ -88197,11 +88281,11 @@ | ||
| 88197 | 88281 | /* Inserting a single row into a parent table cannot cause an immediate |
| 88198 | 88282 | ** foreign key violation. So do nothing in this case. */ |
| 88199 | 88283 | continue; |
| 88200 | 88284 | } |
| 88201 | 88285 | |
| 88202 | - if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ | |
| 88286 | + if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ | |
| 88203 | 88287 | if( !isIgnoreErrors || db->mallocFailed ) return; |
| 88204 | 88288 | continue; |
| 88205 | 88289 | } |
| 88206 | 88290 | assert( aiCol || pFKey->nCol==1 ); |
| 88207 | 88291 | |
| @@ -88252,11 +88336,11 @@ | ||
| 88252 | 88336 | for(p=pTab->pFKey; p; p=p->pNextFrom){ |
| 88253 | 88337 | for(i=0; i<p->nCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom); |
| 88254 | 88338 | } |
| 88255 | 88339 | for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ |
| 88256 | 88340 | Index *pIdx = 0; |
| 88257 | - locateFkeyIndex(pParse, pTab, p, &pIdx, 0); | |
| 88341 | + sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0); | |
| 88258 | 88342 | if( pIdx ){ |
| 88259 | 88343 | for(i=0; i<pIdx->nColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]); |
| 88260 | 88344 | } |
| 88261 | 88345 | } |
| 88262 | 88346 | } |
| @@ -88378,11 +88462,11 @@ | ||
| 88378 | 88462 | ExprList *pList = 0; /* Changes list if ON UPDATE CASCADE */ |
| 88379 | 88463 | Select *pSelect = 0; /* If RESTRICT, "SELECT RAISE(...)" */ |
| 88380 | 88464 | int i; /* Iterator variable */ |
| 88381 | 88465 | Expr *pWhen = 0; /* WHEN clause for the trigger */ |
| 88382 | 88466 | |
| 88383 | - if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0; | |
| 88467 | + if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0; | |
| 88384 | 88468 | assert( aiCol || pFKey->nCol==1 ); |
| 88385 | 88469 | |
| 88386 | 88470 | for(i=0; i<pFKey->nCol; i++){ |
| 88387 | 88471 | Token tOld = { "old", 3 }; /* Literal "old" token */ |
| 88388 | 88472 | Token tNew = { "new", 3 }; /* Literal "new" token */ |
| @@ -92718,13 +92802,15 @@ | ||
| 92718 | 92802 | if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){ |
| 92719 | 92803 | Table *pTab; |
| 92720 | 92804 | if( sqlite3ReadSchema(pParse) ) goto pragma_out; |
| 92721 | 92805 | pTab = sqlite3FindTable(db, zRight, zDb); |
| 92722 | 92806 | if( pTab ){ |
| 92723 | - int i; | |
| 92807 | + int i, k; | |
| 92724 | 92808 | int nHidden = 0; |
| 92725 | 92809 | Column *pCol; |
| 92810 | + Index *pPk; | |
| 92811 | + for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){} | |
| 92726 | 92812 | sqlite3VdbeSetNumCols(v, 6); |
| 92727 | 92813 | pParse->nMem = 6; |
| 92728 | 92814 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC); |
| 92729 | 92815 | sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); |
| 92730 | 92816 | sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC); |
| @@ -92745,12 +92831,18 @@ | ||
| 92745 | 92831 | if( pCol->zDflt ){ |
| 92746 | 92832 | sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pCol->zDflt, 0); |
| 92747 | 92833 | }else{ |
| 92748 | 92834 | sqlite3VdbeAddOp2(v, OP_Null, 0, 5); |
| 92749 | 92835 | } |
| 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); | |
| 92752 | 92844 | sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); |
| 92753 | 92845 | } |
| 92754 | 92846 | } |
| 92755 | 92847 | }else |
| 92756 | 92848 | |
| @@ -92881,10 +92973,124 @@ | ||
| 92881 | 92973 | ++i; |
| 92882 | 92974 | pFK = pFK->pNextFrom; |
| 92883 | 92975 | } |
| 92884 | 92976 | } |
| 92885 | 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 | + } | |
| 92886 | 93092 | }else |
| 92887 | 93093 | #endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */ |
| 92888 | 93094 | |
| 92889 | 93095 | #ifndef NDEBUG |
| 92890 | 93096 | if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){ |
| @@ -93381,11 +93587,11 @@ | ||
| 93381 | 93587 | sqlite3_rekey(db, zKey, i/2); |
| 93382 | 93588 | } |
| 93383 | 93589 | }else |
| 93384 | 93590 | #endif |
| 93385 | 93591 | #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) |
| 93386 | - if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){ | |
| 93592 | + if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){ | |
| 93387 | 93593 | #ifdef SQLITE_HAS_CODEC |
| 93388 | 93594 | if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ |
| 93389 | 93595 | sqlite3_activate_see(&zRight[4]); |
| 93390 | 93596 | } |
| 93391 | 93597 | #endif |
| @@ -94340,11 +94546,11 @@ | ||
| 94340 | 94546 | SrcList *pSrc, /* the FROM clause -- which tables to scan */ |
| 94341 | 94547 | Expr *pWhere, /* the WHERE clause */ |
| 94342 | 94548 | ExprList *pGroupBy, /* the GROUP BY clause */ |
| 94343 | 94549 | Expr *pHaving, /* the HAVING clause */ |
| 94344 | 94550 | 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 */ | |
| 94346 | 94552 | Expr *pLimit, /* LIMIT value. NULL means not used */ |
| 94347 | 94553 | Expr *pOffset /* OFFSET value. NULL means no offset */ |
| 94348 | 94554 | ){ |
| 94349 | 94555 | Select *pNew; |
| 94350 | 94556 | Select standin; |
| @@ -94364,11 +94570,11 @@ | ||
| 94364 | 94570 | pNew->pSrc = pSrc; |
| 94365 | 94571 | pNew->pWhere = pWhere; |
| 94366 | 94572 | pNew->pGroupBy = pGroupBy; |
| 94367 | 94573 | pNew->pHaving = pHaving; |
| 94368 | 94574 | pNew->pOrderBy = pOrderBy; |
| 94369 | - pNew->selFlags = isDistinct ? SF_Distinct : 0; | |
| 94575 | + pNew->selFlags = selFlags; | |
| 94370 | 94576 | pNew->op = TK_SELECT; |
| 94371 | 94577 | pNew->pLimit = pLimit; |
| 94372 | 94578 | pNew->pOffset = pOffset; |
| 94373 | 94579 | assert( pOffset==0 || pLimit!=0 ); |
| 94374 | 94580 | pNew->addrOpenEphm[0] = -1; |
| @@ -95621,12 +95827,10 @@ | ||
| 95621 | 95827 | |
| 95622 | 95828 | for(i=0, pCol=aCol; i<nCol; i++, pCol++){ |
| 95623 | 95829 | /* Get an appropriate name for the column |
| 95624 | 95830 | */ |
| 95625 | 95831 | 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 | 95832 | if( (zName = pEList->a[i].zName)!=0 ){ |
| 95629 | 95833 | /* If the column contains an "AS <name>" phrase, use <name> as the name */ |
| 95630 | 95834 | zName = sqlite3DbStrDup(db, zName); |
| 95631 | 95835 | }else{ |
| 95632 | 95836 | Expr *pColExpr = p; /* The expression that is the result column name */ |
| @@ -95660,10 +95864,13 @@ | ||
| 95660 | 95864 | */ |
| 95661 | 95865 | nName = sqlite3Strlen30(zName); |
| 95662 | 95866 | for(j=cnt=0; j<i; j++){ |
| 95663 | 95867 | if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){ |
| 95664 | 95868 | char *zNewName; |
| 95869 | + int k; | |
| 95870 | + for(k=nName-1; k>1 && sqlite3Isdigit(zName[k]); k--){} | |
| 95871 | + if( zName[k]==':' ) nName = k; | |
| 95665 | 95872 | zName[nName] = 0; |
| 95666 | 95873 | zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt); |
| 95667 | 95874 | sqlite3DbFree(db, zName); |
| 95668 | 95875 | zName = zNewName; |
| 95669 | 95876 | j = -1; |
| @@ -97445,38 +97652,47 @@ | ||
| 97445 | 97652 | return 1; |
| 97446 | 97653 | } |
| 97447 | 97654 | #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ |
| 97448 | 97655 | |
| 97449 | 97656 | /* |
| 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; | |
| 97478 | 97694 | } |
| 97479 | 97695 | |
| 97480 | 97696 | /* |
| 97481 | 97697 | ** The select statement passed as the first argument is an aggregate query. |
| 97482 | 97698 | ** The second argment is the associated aggregate-info object. This |
| @@ -97567,10 +97783,11 @@ | ||
| 97567 | 97783 | int i, j, k; |
| 97568 | 97784 | SrcList *pTabList; |
| 97569 | 97785 | ExprList *pEList; |
| 97570 | 97786 | struct SrcList_item *pFrom; |
| 97571 | 97787 | sqlite3 *db = pParse->db; |
| 97788 | + Expr *pE, *pRight, *pExpr; | |
| 97572 | 97789 | |
| 97573 | 97790 | if( db->mallocFailed ){ |
| 97574 | 97791 | return WRC_Abort; |
| 97575 | 97792 | } |
| 97576 | 97793 | if( NEVER(p->pSrc==0) || (p->selFlags & SF_Expanded)!=0 ){ |
| @@ -97652,11 +97869,11 @@ | ||
| 97652 | 97869 | ** |
| 97653 | 97870 | ** The first loop just checks to see if there are any "*" operators |
| 97654 | 97871 | ** that need expanding. |
| 97655 | 97872 | */ |
| 97656 | 97873 | for(k=0; k<pEList->nExpr; k++){ |
| 97657 | - Expr *pE = pEList->a[k].pExpr; | |
| 97874 | + pE = pEList->a[k].pExpr; | |
| 97658 | 97875 | if( pE->op==TK_ALL ) break; |
| 97659 | 97876 | assert( pE->op!=TK_DOT || pE->pRight!=0 ); |
| 97660 | 97877 | assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) ); |
| 97661 | 97878 | if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break; |
| 97662 | 97879 | } |
| @@ -97670,14 +97887,22 @@ | ||
| 97670 | 97887 | ExprList *pNew = 0; |
| 97671 | 97888 | int flags = pParse->db->flags; |
| 97672 | 97889 | int longNames = (flags & SQLITE_FullColNames)!=0 |
| 97673 | 97890 | && (flags & SQLITE_ShortColNames)==0; |
| 97674 | 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 | + | |
| 97675 | 97899 | 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) ){ | |
| 97679 | 97904 | /* This particular expression does not need to be expanded. |
| 97680 | 97905 | */ |
| 97681 | 97906 | pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); |
| 97682 | 97907 | if( pNew ){ |
| 97683 | 97908 | pNew->a[pNew->nExpr-1].zName = a[k].zName; |
| @@ -97688,44 +97913,56 @@ | ||
| 97688 | 97913 | a[k].pExpr = 0; |
| 97689 | 97914 | }else{ |
| 97690 | 97915 | /* This expression is a "*" or a "TABLE.*" and needs to be |
| 97691 | 97916 | ** expanded. */ |
| 97692 | 97917 | 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 */ | |
| 97694 | 97919 | if( pE->op==TK_DOT ){ |
| 97695 | 97920 | assert( pE->pLeft!=0 ); |
| 97696 | 97921 | assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); |
| 97697 | 97922 | zTName = pE->pLeft->u.zToken; |
| 97698 | - }else{ | |
| 97699 | - zTName = 0; | |
| 97700 | 97923 | } |
| 97701 | 97924 | for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ |
| 97702 | 97925 | Table *pTab = pFrom->pTab; |
| 97926 | + Select *pSub = pFrom->pSelect; | |
| 97703 | 97927 | char *zTabName = pFrom->zAlias; |
| 97928 | + const char *zSchemaName = 0; | |
| 97929 | + int iDb; | |
| 97704 | 97930 | if( zTabName==0 ){ |
| 97705 | 97931 | zTabName = pTab->zName; |
| 97706 | 97932 | } |
| 97707 | 97933 | 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 : "*"; | |
| 97710 | 97941 | } |
| 97711 | - tableSeen = 1; | |
| 97712 | 97942 | for(j=0; j<pTab->nCol; j++){ |
| 97713 | - Expr *pExpr, *pRight; | |
| 97714 | 97943 | char *zName = pTab->aCol[j].zName; |
| 97715 | 97944 | char *zColname; /* The computed column name */ |
| 97716 | 97945 | char *zToFree; /* Malloced string that needs to be freed */ |
| 97717 | 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 | + } | |
| 97718 | 97954 | |
| 97719 | 97955 | /* If a column is marked as 'hidden' (currently only possible |
| 97720 | 97956 | ** for virtual tables), do not include it in the expanded |
| 97721 | 97957 | ** result-set list. |
| 97722 | 97958 | */ |
| 97723 | 97959 | if( IsHiddenColumn(&pTab->aCol[j]) ){ |
| 97724 | 97960 | assert(IsVirtual(pTab)); |
| 97725 | 97961 | continue; |
| 97726 | 97962 | } |
| 97963 | + tableSeen = 1; | |
| 97727 | 97964 | |
| 97728 | 97965 | if( i>0 && zTName==0 ){ |
| 97729 | 97966 | if( (pFrom->jointype & JT_NATURAL)!=0 |
| 97730 | 97967 | && tableAndColumnIndex(pTabList, i, zName, 0, 0) |
| 97731 | 97968 | ){ |
| @@ -97744,10 +97981,14 @@ | ||
| 97744 | 97981 | zToFree = 0; |
| 97745 | 97982 | if( longNames || pTabList->nSrc>1 ){ |
| 97746 | 97983 | Expr *pLeft; |
| 97747 | 97984 | pLeft = sqlite3Expr(db, TK_ID, zTabName); |
| 97748 | 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 | + } | |
| 97749 | 97990 | if( longNames ){ |
| 97750 | 97991 | zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName); |
| 97751 | 97992 | zToFree = zColname; |
| 97752 | 97993 | } |
| 97753 | 97994 | }else{ |
| @@ -97755,10 +97996,22 @@ | ||
| 97755 | 97996 | } |
| 97756 | 97997 | pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); |
| 97757 | 97998 | sColname.z = zColname; |
| 97758 | 97999 | sColname.n = sqlite3Strlen30(zColname); |
| 97759 | 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 | + } | |
| 97760 | 98013 | sqlite3DbFree(db, zToFree); |
| 97761 | 98014 | } |
| 97762 | 98015 | } |
| 97763 | 98016 | if( !tableSeen ){ |
| 97764 | 98017 | if( zTName ){ |
| @@ -98812,15 +99065,21 @@ | ||
| 98812 | 99065 | ** index or indices to use) should place a different priority on |
| 98813 | 99066 | ** satisfying the 'ORDER BY' clause than it does in other cases. |
| 98814 | 99067 | ** Refer to code and comments in where.c for details. |
| 98815 | 99068 | */ |
| 98816 | 99069 | 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 | + | |
| 98818 | 99079 | 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); | |
| 98822 | 99081 | pDel = pMinMax; |
| 98823 | 99082 | if( pMinMax && !db->mallocFailed ){ |
| 98824 | 99083 | pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0; |
| 98825 | 99084 | pMinMax->a[0].pExpr->op = TK_COLUMN; |
| 98826 | 99085 | } |
| @@ -102549,12 +102808,12 @@ | ||
| 102549 | 102808 | Expr *pExpr; /* Pointer to the subexpression that is this term */ |
| 102550 | 102809 | int iParent; /* Disable pWC->a[iParent] when this term disabled */ |
| 102551 | 102810 | int leftCursor; /* Cursor number of X in "X <op> <expr>" */ |
| 102552 | 102811 | union { |
| 102553 | 102812 | 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 */ | |
| 102556 | 102815 | } u; |
| 102557 | 102816 | u16 eOperator; /* A WO_xx value describing <op> */ |
| 102558 | 102817 | u8 wtFlags; /* TERM_xxx bit flags. See below */ |
| 102559 | 102818 | u8 nChild; /* Number of children that must disable us */ |
| 102560 | 102819 | WhereClause *pWC; /* The clause this term is part of */ |
| @@ -102678,10 +102937,11 @@ | ||
| 102678 | 102937 | #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) |
| 102679 | 102938 | #define WO_MATCH 0x040 |
| 102680 | 102939 | #define WO_ISNULL 0x080 |
| 102681 | 102940 | #define WO_OR 0x100 /* Two or more OR-connected terms */ |
| 102682 | 102941 | #define WO_AND 0x200 /* Two or more AND-connected terms */ |
| 102942 | +#define WO_EQUIV 0x400 /* Of the form A==B, both columns */ | |
| 102683 | 102943 | #define WO_NOOP 0x800 /* This term does not restrict search space */ |
| 102684 | 102944 | |
| 102685 | 102945 | #define WO_ALL 0xfff /* Mask of all possible WO_* values */ |
| 102686 | 102946 | #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ |
| 102687 | 102947 | |
| @@ -102704,11 +102964,11 @@ | ||
| 102704 | 102964 | #define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */ |
| 102705 | 102965 | #define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */ |
| 102706 | 102966 | #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ |
| 102707 | 102967 | #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ |
| 102708 | 102968 | #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 */ | |
| 102710 | 102970 | #define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */ |
| 102711 | 102971 | #define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */ |
| 102712 | 102972 | #define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */ |
| 102713 | 102973 | #define WHERE_IDX_ONLY 0x00400000 /* Use index only - omit table */ |
| 102714 | 102974 | #define WHERE_ORDERED 0x00800000 /* Output will appear in correct order */ |
| @@ -102854,11 +103114,11 @@ | ||
| 102854 | 103114 | sqlite3DbFree(db, pOld); |
| 102855 | 103115 | } |
| 102856 | 103116 | pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]); |
| 102857 | 103117 | } |
| 102858 | 103118 | pTerm = &pWC->a[idx = pWC->nTerm++]; |
| 102859 | - pTerm->pExpr = p; | |
| 103119 | + pTerm->pExpr = sqlite3ExprSkipCollate(p); | |
| 102860 | 103120 | pTerm->wtFlags = wtFlags; |
| 102861 | 103121 | pTerm->pWC = pWC; |
| 102862 | 103122 | pTerm->iParent = -1; |
| 102863 | 103123 | return idx; |
| 102864 | 103124 | } |
| @@ -103080,58 +103340,112 @@ | ||
| 103080 | 103340 | /* |
| 103081 | 103341 | ** Search for a term in the WHERE clause that is of the form "X <op> <expr>" |
| 103082 | 103342 | ** where X is a reference to the iColumn of table iCur and <op> is one of |
| 103083 | 103343 | ** the WO_xx operator codes specified by the op parameter. |
| 103084 | 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. | |
| 103085 | 103363 | */ |
| 103086 | 103364 | static WhereTerm *findTerm( |
| 103087 | 103365 | WhereClause *pWC, /* The WHERE clause to be searched */ |
| 103088 | 103366 | int iCur, /* Cursor number of LHS */ |
| 103089 | 103367 | int iColumn, /* Column number of LHS */ |
| 103090 | 103368 | Bitmask notReady, /* RHS must not overlap with this mask */ |
| 103091 | 103369 | u32 op, /* Mask of WO_xx values describing operator */ |
| 103092 | 103370 | Index *pIdx /* Must be compatible with this index, if not NULL */ |
| 103093 | 103371 | ){ |
| 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 | + | |
| 103096 | 103383 | 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; | |
| 103133 | 103447 | } |
| 103134 | 103448 | |
| 103135 | 103449 | /* Forward reference */ |
| 103136 | 103450 | static void exprAnalyze(SrcList*, WhereClause*, int); |
| 103137 | 103451 | |
| @@ -103405,11 +103719,10 @@ | ||
| 103405 | 103719 | indexable = ~(Bitmask)0; |
| 103406 | 103720 | chngToIN = ~(pWC->vmask); |
| 103407 | 103721 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){ |
| 103408 | 103722 | if( (pOrTerm->eOperator & WO_SINGLE)==0 ){ |
| 103409 | 103723 | WhereAndInfo *pAndInfo; |
| 103410 | - assert( pOrTerm->eOperator==0 ); | |
| 103411 | 103724 | assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 ); |
| 103412 | 103725 | chngToIN = 0; |
| 103413 | 103726 | pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo)); |
| 103414 | 103727 | if( pAndInfo ){ |
| 103415 | 103728 | WhereClause *pAndWC; |
| @@ -103444,11 +103757,11 @@ | ||
| 103444 | 103757 | if( pOrTerm->wtFlags & TERM_VIRTUAL ){ |
| 103445 | 103758 | WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent]; |
| 103446 | 103759 | b |= getMask(pMaskSet, pOther->leftCursor); |
| 103447 | 103760 | } |
| 103448 | 103761 | indexable &= b; |
| 103449 | - if( pOrTerm->eOperator!=WO_EQ ){ | |
| 103762 | + if( (pOrTerm->eOperator & WO_EQ)==0 ){ | |
| 103450 | 103763 | chngToIN = 0; |
| 103451 | 103764 | }else{ |
| 103452 | 103765 | chngToIN &= b; |
| 103453 | 103766 | } |
| 103454 | 103767 | } |
| @@ -103495,11 +103808,11 @@ | ||
| 103495 | 103808 | ** and column is found but leave okToChngToIN false if not found. |
| 103496 | 103809 | */ |
| 103497 | 103810 | for(j=0; j<2 && !okToChngToIN; j++){ |
| 103498 | 103811 | pOrTerm = pOrWc->a; |
| 103499 | 103812 | for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ |
| 103500 | - assert( pOrTerm->eOperator==WO_EQ ); | |
| 103813 | + assert( pOrTerm->eOperator & WO_EQ ); | |
| 103501 | 103814 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103502 | 103815 | if( pOrTerm->leftCursor==iCursor ){ |
| 103503 | 103816 | /* This is the 2-bit case and we are on the second iteration and |
| 103504 | 103817 | ** current term is from the first iteration. So skip this term. */ |
| 103505 | 103818 | assert( j==1 ); |
| @@ -103521,21 +103834,21 @@ | ||
| 103521 | 103834 | } |
| 103522 | 103835 | if( i<0 ){ |
| 103523 | 103836 | /* No candidate table+column was found. This can only occur |
| 103524 | 103837 | ** on the second iteration */ |
| 103525 | 103838 | assert( j==1 ); |
| 103526 | - assert( (chngToIN&(chngToIN-1))==0 ); | |
| 103839 | + assert( IsPowerOfTwo(chngToIN) ); | |
| 103527 | 103840 | assert( chngToIN==getMask(pMaskSet, iCursor) ); |
| 103528 | 103841 | break; |
| 103529 | 103842 | } |
| 103530 | 103843 | testcase( j==1 ); |
| 103531 | 103844 | |
| 103532 | 103845 | /* We have found a candidate table and column. Check to see if that |
| 103533 | 103846 | ** table and column is common to every term in the OR clause */ |
| 103534 | 103847 | okToChngToIN = 1; |
| 103535 | 103848 | for(; i>=0 && okToChngToIN; i--, pOrTerm++){ |
| 103536 | - assert( pOrTerm->eOperator==WO_EQ ); | |
| 103849 | + assert( pOrTerm->eOperator & WO_EQ ); | |
| 103537 | 103850 | if( pOrTerm->leftCursor!=iCursor ){ |
| 103538 | 103851 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103539 | 103852 | }else if( pOrTerm->u.leftColumn!=iColumn ){ |
| 103540 | 103853 | okToChngToIN = 0; |
| 103541 | 103854 | }else{ |
| @@ -103567,11 +103880,11 @@ | ||
| 103567 | 103880 | Expr *pLeft = 0; /* The LHS of the IN operator */ |
| 103568 | 103881 | Expr *pNew; /* The complete IN operator */ |
| 103569 | 103882 | |
| 103570 | 103883 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ |
| 103571 | 103884 | if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; |
| 103572 | - assert( pOrTerm->eOperator==WO_EQ ); | |
| 103885 | + assert( pOrTerm->eOperator & WO_EQ ); | |
| 103573 | 103886 | assert( pOrTerm->leftCursor==iCursor ); |
| 103574 | 103887 | assert( pOrTerm->u.leftColumn==iColumn ); |
| 103575 | 103888 | pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); |
| 103576 | 103889 | pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup); |
| 103577 | 103890 | pLeft = pOrTerm->pExpr->pLeft; |
| @@ -103596,11 +103909,10 @@ | ||
| 103596 | 103909 | pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ |
| 103597 | 103910 | } |
| 103598 | 103911 | } |
| 103599 | 103912 | } |
| 103600 | 103913 | #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ |
| 103601 | - | |
| 103602 | 103914 | |
| 103603 | 103915 | /* |
| 103604 | 103916 | ** The input to this routine is an WhereTerm structure with only the |
| 103605 | 103917 | ** "pExpr" field filled in. The job of this routine is to analyze the |
| 103606 | 103918 | ** subexpression and populate all the other fields of the WhereTerm |
| @@ -103639,11 +103951,12 @@ | ||
| 103639 | 103951 | if( db->mallocFailed ){ |
| 103640 | 103952 | return; |
| 103641 | 103953 | } |
| 103642 | 103954 | pTerm = &pWC->a[idxTerm]; |
| 103643 | 103955 | pMaskSet = pWC->pMaskSet; |
| 103644 | - pExpr = sqlite3ExprSkipCollate(pTerm->pExpr); | |
| 103956 | + pExpr = pTerm->pExpr; | |
| 103957 | + assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE ); | |
| 103645 | 103958 | prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft); |
| 103646 | 103959 | op = pExpr->op; |
| 103647 | 103960 | if( op==TK_IN ){ |
| 103648 | 103961 | assert( pExpr->pRight==0 ); |
| 103649 | 103962 | if( ExprHasProperty(pExpr, EP_xIsSelect) ){ |
| @@ -103665,21 +103978,23 @@ | ||
| 103665 | 103978 | } |
| 103666 | 103979 | pTerm->prereqAll = prereqAll; |
| 103667 | 103980 | pTerm->leftCursor = -1; |
| 103668 | 103981 | pTerm->iParent = -1; |
| 103669 | 103982 | pTerm->eOperator = 0; |
| 103670 | - if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){ | |
| 103983 | + if( allowedOp(op) ){ | |
| 103671 | 103984 | Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); |
| 103672 | 103985 | Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); |
| 103986 | + u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; | |
| 103673 | 103987 | if( pLeft->op==TK_COLUMN ){ |
| 103674 | 103988 | pTerm->leftCursor = pLeft->iTable; |
| 103675 | 103989 | pTerm->u.leftColumn = pLeft->iColumn; |
| 103676 | - pTerm->eOperator = operatorMask(op); | |
| 103990 | + pTerm->eOperator = operatorMask(op) & opMask; | |
| 103677 | 103991 | } |
| 103678 | 103992 | if( pRight && pRight->op==TK_COLUMN ){ |
| 103679 | 103993 | WhereTerm *pNew; |
| 103680 | 103994 | Expr *pDup; |
| 103995 | + u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */ | |
| 103681 | 103996 | if( pTerm->leftCursor>=0 ){ |
| 103682 | 103997 | int idxNew; |
| 103683 | 103998 | pDup = sqlite3ExprDup(db, pExpr, 0); |
| 103684 | 103999 | if( db->mallocFailed ){ |
| 103685 | 104000 | sqlite3ExprDelete(db, pDup); |
| @@ -103690,10 +104005,17 @@ | ||
| 103690 | 104005 | pNew = &pWC->a[idxNew]; |
| 103691 | 104006 | pNew->iParent = idxTerm; |
| 103692 | 104007 | pTerm = &pWC->a[idxTerm]; |
| 103693 | 104008 | pTerm->nChild = 1; |
| 103694 | 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 | + } | |
| 103695 | 104017 | }else{ |
| 103696 | 104018 | pDup = pExpr; |
| 103697 | 104019 | pNew = pTerm; |
| 103698 | 104020 | } |
| 103699 | 104021 | exprCommute(pParse, pDup); |
| @@ -103701,11 +104023,11 @@ | ||
| 103701 | 104023 | pNew->leftCursor = pLeft->iTable; |
| 103702 | 104024 | pNew->u.leftColumn = pLeft->iColumn; |
| 103703 | 104025 | testcase( (prereqLeft | extraRight) != prereqLeft ); |
| 103704 | 104026 | pNew->prereqRight = prereqLeft | extraRight; |
| 103705 | 104027 | pNew->prereqAll = prereqAll; |
| 103706 | - pNew->eOperator = operatorMask(pDup->op); | |
| 104028 | + pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; | |
| 103707 | 104029 | } |
| 103708 | 104030 | } |
| 103709 | 104031 | |
| 103710 | 104032 | #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION |
| 103711 | 104033 | /* If a term is the BETWEEN operator, create two new virtual terms |
| @@ -104160,11 +104482,11 @@ | ||
| 104160 | 104482 | return; |
| 104161 | 104483 | } |
| 104162 | 104484 | |
| 104163 | 104485 | /* Search the WHERE clause terms for a usable WO_OR term. */ |
| 104164 | 104486 | for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ |
| 104165 | - if( pTerm->eOperator==WO_OR | |
| 104487 | + if( (pTerm->eOperator & WO_OR)!=0 | |
| 104166 | 104488 | && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0 |
| 104167 | 104489 | && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 |
| 104168 | 104490 | ){ |
| 104169 | 104491 | WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; |
| 104170 | 104492 | WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; |
| @@ -104181,11 +104503,11 @@ | ||
| 104181 | 104503 | sBOI.ppIdxInfo = 0; |
| 104182 | 104504 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ |
| 104183 | 104505 | WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", |
| 104184 | 104506 | (pOrTerm - pOrWC->a), (pTerm - pWC->a) |
| 104185 | 104507 | )); |
| 104186 | - if( pOrTerm->eOperator==WO_AND ){ | |
| 104508 | + if( (pOrTerm->eOperator& WO_AND)!=0 ){ | |
| 104187 | 104509 | sBOI.pWC = &pOrTerm->u.pAndInfo->wc; |
| 104188 | 104510 | bestIndex(&sBOI); |
| 104189 | 104511 | }else if( pOrTerm->leftCursor==iCur ){ |
| 104190 | 104512 | WhereClause tempWC; |
| 104191 | 104513 | tempWC.pParse = pWC->pParse; |
| @@ -104242,11 +104564,11 @@ | ||
| 104242 | 104564 | struct SrcList_item *pSrc, /* Table we are trying to access */ |
| 104243 | 104565 | Bitmask notReady /* Tables in outer loops of the join */ |
| 104244 | 104566 | ){ |
| 104245 | 104567 | char aff; |
| 104246 | 104568 | if( pTerm->leftCursor!=pSrc->iCursor ) return 0; |
| 104247 | - if( pTerm->eOperator!=WO_EQ ) return 0; | |
| 104569 | + if( (pTerm->eOperator & WO_EQ)==0 ) return 0; | |
| 104248 | 104570 | if( (pTerm->prereqRight & notReady)!=0 ) return 0; |
| 104249 | 104571 | aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; |
| 104250 | 104572 | if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; |
| 104251 | 104573 | return 1; |
| 104252 | 104574 | } |
| @@ -104504,14 +104826,14 @@ | ||
| 104504 | 104826 | |
| 104505 | 104827 | /* Count the number of possible WHERE clause constraints referring |
| 104506 | 104828 | ** to this virtual table */ |
| 104507 | 104829 | for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104508 | 104830 | 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; | |
| 104513 | 104835 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104514 | 104836 | nTerm++; |
| 104515 | 104837 | } |
| 104516 | 104838 | |
| 104517 | 104839 | /* If the ORDER BY clause contains only columns in the current |
| @@ -104555,29 +104877,32 @@ | ||
| 104555 | 104877 | *(struct sqlite3_index_orderby**)&pIdxInfo->aOrderBy = pIdxOrderBy; |
| 104556 | 104878 | *(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage = |
| 104557 | 104879 | pUsage; |
| 104558 | 104880 | |
| 104559 | 104881 | for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104882 | + u8 op; | |
| 104560 | 104883 | 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; | |
| 104565 | 104888 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104566 | 104889 | pIdxCons[j].iColumn = pTerm->u.leftColumn; |
| 104567 | 104890 | 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; | |
| 104569 | 104894 | /* The direct assignment in the previous line is possible only because |
| 104570 | 104895 | ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The |
| 104571 | 104896 | ** following asserts verify this fact. */ |
| 104572 | 104897 | assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ ); |
| 104573 | 104898 | assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT ); |
| 104574 | 104899 | assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE ); |
| 104575 | 104900 | assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); |
| 104576 | 104901 | assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); |
| 104577 | 104902 | 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) ); | |
| 104579 | 104904 | j++; |
| 104580 | 104905 | } |
| 104581 | 104906 | for(i=0; i<nOrderBy; i++){ |
| 104582 | 104907 | Expr *pExpr = pOrderBy->a[i].pExpr; |
| 104583 | 104908 | pIdxOrderBy[i].iColumn = pExpr->iColumn; |
| @@ -104659,10 +104984,11 @@ | ||
| 104659 | 104984 | struct sqlite3_index_constraint *pIdxCons; |
| 104660 | 104985 | struct sqlite3_index_constraint_usage *pUsage; |
| 104661 | 104986 | WhereTerm *pTerm; |
| 104662 | 104987 | int i, j; |
| 104663 | 104988 | int nOrderBy; |
| 104989 | + int bAllowIN; /* Allow IN optimizations */ | |
| 104664 | 104990 | double rCost; |
| 104665 | 104991 | |
| 104666 | 104992 | /* Make sure wsFlags is initialized to some sane value. Otherwise, if the |
| 104667 | 104993 | ** malloc in allocateIndexInfo() fails and this function returns leaving |
| 104668 | 104994 | ** wsFlags in an uninitialized state, the caller may behave unpredictably. |
| @@ -104693,63 +105019,91 @@ | ||
| 104693 | 105019 | ** sqlite3ViewGetColumnNames() would have picked up the error. |
| 104694 | 105020 | */ |
| 104695 | 105021 | assert( pTab->azModuleArg && pTab->azModuleArg[0] ); |
| 104696 | 105022 | assert( sqlite3GetVTable(pParse->db, pTab) ); |
| 104697 | 105023 | |
| 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 | + | |
| 104751 | 105105 | /* If there is an ORDER BY clause, and the selected virtual table index |
| 104752 | 105106 | ** does not satisfy it, increase the cost of the scan accordingly. This |
| 104753 | 105107 | ** matches the processing for non-virtual tables in bestBtreeIndex(). |
| 104754 | 105108 | */ |
| 104755 | 105109 | rCost = pIdxInfo->estimatedCost; |
| @@ -105040,28 +105394,28 @@ | ||
| 105040 | 105394 | u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; |
| 105041 | 105395 | |
| 105042 | 105396 | if( pLower ){ |
| 105043 | 105397 | Expr *pExpr = pLower->pExpr->pRight; |
| 105044 | 105398 | 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 ); | |
| 105046 | 105400 | if( rc==SQLITE_OK |
| 105047 | 105401 | && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK |
| 105048 | 105402 | ){ |
| 105049 | 105403 | iLower = a[0]; |
| 105050 | - if( pLower->eOperator==WO_GT ) iLower += a[1]; | |
| 105404 | + if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1]; | |
| 105051 | 105405 | } |
| 105052 | 105406 | sqlite3ValueFree(pRangeVal); |
| 105053 | 105407 | } |
| 105054 | 105408 | if( rc==SQLITE_OK && pUpper ){ |
| 105055 | 105409 | Expr *pExpr = pUpper->pExpr->pRight; |
| 105056 | 105410 | 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 ); | |
| 105058 | 105412 | if( rc==SQLITE_OK |
| 105059 | 105413 | && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK |
| 105060 | 105414 | ){ |
| 105061 | 105415 | iUpper = a[0]; |
| 105062 | - if( pUpper->eOperator==WO_LE ) iUpper += a[1]; | |
| 105416 | + if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1]; | |
| 105063 | 105417 | } |
| 105064 | 105418 | sqlite3ValueFree(pRangeVal); |
| 105065 | 105419 | } |
| 105066 | 105420 | if( rc==SQLITE_OK ){ |
| 105067 | 105421 | if( iUpper<=iLower ){ |
| @@ -105365,16 +105719,16 @@ | ||
| 105365 | 105719 | ** if there are any X= or X IS NULL constraints in the WHERE clause. */ |
| 105366 | 105720 | pConstraint = findTerm(p->pWC, base, iColumn, p->notReady, |
| 105367 | 105721 | WO_EQ|WO_ISNULL|WO_IN, pIdx); |
| 105368 | 105722 | if( pConstraint==0 ){ |
| 105369 | 105723 | isEq = 0; |
| 105370 | - }else if( pConstraint->eOperator==WO_IN ){ | |
| 105724 | + }else if( (pConstraint->eOperator & WO_IN)!=0 ){ | |
| 105371 | 105725 | /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY |
| 105372 | 105726 | ** because we do not know in what order the values on the RHS of the IN |
| 105373 | 105727 | ** operator will occur. */ |
| 105374 | 105728 | break; |
| 105375 | - }else if( pConstraint->eOperator==WO_ISNULL ){ | |
| 105729 | + }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){ | |
| 105376 | 105730 | uniqueNotNull = 0; |
| 105377 | 105731 | isEq = 1; /* "X IS NULL" means X has only a single value */ |
| 105378 | 105732 | }else if( pConstraint->prereqRight==0 ){ |
| 105379 | 105733 | isEq = 1; /* Constraint "X=constant" means X has only a single value */ |
| 105380 | 105734 | }else{ |
| @@ -105720,11 +106074,11 @@ | ||
| 105720 | 106074 | int bRev = 2; |
| 105721 | 106075 | WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat)); |
| 105722 | 106076 | pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev); |
| 105723 | 106077 | WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n", |
| 105724 | 106078 | 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 ){ | |
| 105726 | 106080 | pc.plan.wsFlags |= WHERE_ORDERED; |
| 105727 | 106081 | } |
| 105728 | 106082 | if( nOrderBy==pc.plan.nOBSat ){ |
| 105729 | 106083 | bSort = 0; |
| 105730 | 106084 | pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE; |
| @@ -105783,16 +106137,17 @@ | ||
| 105783 | 106137 | */ |
| 105784 | 106138 | if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 |
| 105785 | 106139 | && pFirstTerm!=0 && aiRowEst[1]>1 ){ |
| 105786 | 106140 | assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); |
| 105787 | 106141 | 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 ); | |
| 105790 | 106145 | whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, |
| 105791 | 106146 | &pc.plan.nRow); |
| 105792 | 106147 | }else if( bInEst==0 ){ |
| 105793 | - assert( pFirstTerm->eOperator==WO_IN ); | |
| 106148 | + assert( pFirstTerm->eOperator & WO_IN ); | |
| 105794 | 106149 | whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, |
| 105795 | 106150 | &pc.plan.nRow); |
| 105796 | 106151 | } |
| 105797 | 106152 | } |
| 105798 | 106153 | #endif /* SQLITE_ENABLE_STAT3 */ |
| @@ -105935,11 +106290,11 @@ | ||
| 105935 | 106290 | ** more selective intentionally because of the subjective |
| 105936 | 106291 | ** observation that indexed range constraints really are more |
| 105937 | 106292 | ** selective in practice, on average. */ |
| 105938 | 106293 | pc.plan.nRow /= 3; |
| 105939 | 106294 | } |
| 105940 | - }else if( pTerm->eOperator!=WO_NOOP ){ | |
| 106295 | + }else if( (pTerm->eOperator & WO_NOOP)==0 ){ | |
| 105941 | 106296 | /* Any other expression lowers the output row count by half */ |
| 105942 | 106297 | pc.plan.nRow /= 2; |
| 105943 | 106298 | } |
| 105944 | 106299 | } |
| 105945 | 106300 | if( pc.plan.nRow<2 ) pc.plan.nRow = 2; |
| @@ -105987,12 +106342,13 @@ | ||
| 105987 | 106342 | assert( pSrc->pIndex==0 |
| 105988 | 106343 | || p->cost.plan.u.pIdx==0 |
| 105989 | 106344 | || p->cost.plan.u.pIdx==pSrc->pIndex |
| 105990 | 106345 | ); |
| 105991 | 106346 | |
| 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)); | |
| 105994 | 106350 | |
| 105995 | 106351 | bestOrClauseIndex(p); |
| 105996 | 106352 | bestAutomaticIndex(p); |
| 105997 | 106353 | p->cost.plan.wsFlags |= eqTermMask; |
| 105998 | 106354 | } |
| @@ -106514,32 +106870,40 @@ | ||
| 106514 | 106870 | if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ |
| 106515 | 106871 | /* Case 0: The table is a virtual-table. Use the VFilter and VNext |
| 106516 | 106872 | ** to access the data. |
| 106517 | 106873 | */ |
| 106518 | 106874 | int iReg; /* P3 Value for OP_VFilter */ |
| 106875 | + int addrNotFound; | |
| 106519 | 106876 | sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx; |
| 106520 | 106877 | int nConstraint = pVtabIdx->nConstraint; |
| 106521 | 106878 | struct sqlite3_index_constraint_usage *aUsage = |
| 106522 | 106879 | pVtabIdx->aConstraintUsage; |
| 106523 | 106880 | const struct sqlite3_index_constraint *aConstraint = |
| 106524 | 106881 | pVtabIdx->aConstraint; |
| 106525 | 106882 | |
| 106526 | 106883 | sqlite3ExprCachePush(pParse); |
| 106527 | 106884 | iReg = sqlite3GetTempRange(pParse, nConstraint+2); |
| 106885 | + addrNotFound = pLevel->addrBrk; | |
| 106528 | 106886 | for(j=1; j<=nConstraint; j++){ |
| 106529 | 106887 | for(k=0; k<nConstraint; k++){ |
| 106530 | 106888 | 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 | + } | |
| 106533 | 106897 | break; |
| 106534 | 106898 | } |
| 106535 | 106899 | } |
| 106536 | 106900 | if( k==nConstraint ) break; |
| 106537 | 106901 | } |
| 106538 | 106902 | sqlite3VdbeAddOp2(v, OP_Integer, pVtabIdx->idxNum, iReg); |
| 106539 | 106903 | 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, | |
| 106541 | 106905 | pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC); |
| 106542 | 106906 | pVtabIdx->needToFreeIdxStr = 0; |
| 106543 | 106907 | for(j=0; j<nConstraint; j++){ |
| 106544 | 106908 | if( aUsage[j].omit ){ |
| 106545 | 106909 | int iTerm = aConstraint[j].iTermOffset; |
| @@ -106562,11 +106926,10 @@ | ||
| 106562 | 106926 | */ |
| 106563 | 106927 | iReleaseReg = sqlite3GetTempReg(pParse); |
| 106564 | 106928 | pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); |
| 106565 | 106929 | assert( pTerm!=0 ); |
| 106566 | 106930 | assert( pTerm->pExpr!=0 ); |
| 106567 | - assert( pTerm->leftCursor==iCur ); | |
| 106568 | 106931 | assert( omitTable==0 ); |
| 106569 | 106932 | testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ |
| 106570 | 106933 | iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg); |
| 106571 | 106934 | addrNxt = pLevel->addrNxt; |
| 106572 | 106935 | sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); |
| @@ -106953,11 +107316,11 @@ | ||
| 106953 | 107316 | int ii; /* Loop counter */ |
| 106954 | 107317 | Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ |
| 106955 | 107318 | |
| 106956 | 107319 | pTerm = pLevel->plan.u.pTerm; |
| 106957 | 107320 | assert( pTerm!=0 ); |
| 106958 | - assert( pTerm->eOperator==WO_OR ); | |
| 107321 | + assert( pTerm->eOperator & WO_OR ); | |
| 106959 | 107322 | assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); |
| 106960 | 107323 | pOrWc = &pTerm->u.pOrInfo->wc; |
| 106961 | 107324 | pLevel->op = OP_Return; |
| 106962 | 107325 | pLevel->p1 = regReturn; |
| 106963 | 107326 | |
| @@ -107026,11 +107389,11 @@ | ||
| 107026 | 107389 | } |
| 107027 | 107390 | } |
| 107028 | 107391 | |
| 107029 | 107392 | for(ii=0; ii<pOrWc->nTerm; ii++){ |
| 107030 | 107393 | WhereTerm *pOrTerm = &pOrWc->a[ii]; |
| 107031 | - if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ | |
| 107394 | + if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ | |
| 107032 | 107395 | WhereInfo *pSubWInfo; /* Info for single OR-term scan */ |
| 107033 | 107396 | Expr *pOrExpr = pOrTerm->pExpr; |
| 107034 | 107397 | if( pAndExpr ){ |
| 107035 | 107398 | pAndExpr->pLeft = pOrExpr; |
| 107036 | 107399 | pOrExpr = pAndExpr; |
| @@ -107481,10 +107844,11 @@ | ||
| 107481 | 107844 | Index *pIdx; /* Index for FROM table at pTabItem */ |
| 107482 | 107845 | int j; /* For looping over FROM tables */ |
| 107483 | 107846 | int bestJ = -1; /* The value of j */ |
| 107484 | 107847 | Bitmask m; /* Bitmask value for j or bestJ */ |
| 107485 | 107848 | int isOptimal; /* Iterator for optimal/non-optimal search */ |
| 107849 | + int ckOptimal; /* Do the optimal scan check */ | |
| 107486 | 107850 | int nUnconstrained; /* Number tables without INDEXED BY */ |
| 107487 | 107851 | Bitmask notIndexed; /* Mask of tables that cannot use an index */ |
| 107488 | 107852 | |
| 107489 | 107853 | memset(&bestPlan, 0, sizeof(bestPlan)); |
| 107490 | 107854 | bestPlan.rCost = SQLITE_BIG_DBL; |
| @@ -107515,14 +107879,12 @@ | ||
| 107515 | 107879 | ** |
| 107516 | 107880 | ** The second loop iteration is only performed if no optimal scan |
| 107517 | 107881 | ** strategies were found by the first iteration. This second iteration |
| 107518 | 107882 | ** is used to search for the lowest cost scan overall. |
| 107519 | 107883 | ** |
| 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: | |
| 107524 | 107886 | ** |
| 107525 | 107887 | ** CREATE TABLE t1(a, b); |
| 107526 | 107888 | ** CREATE TABLE t2(c, d); |
| 107527 | 107889 | ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; |
| 107528 | 107890 | ** |
| @@ -107533,20 +107895,44 @@ | ||
| 107533 | 107895 | ** algorithm may choose to use t2 for the outer loop, which is a much |
| 107534 | 107896 | ** costlier approach. |
| 107535 | 107897 | */ |
| 107536 | 107898 | nUnconstrained = 0; |
| 107537 | 107899 | 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; | |
| 107539 | 107907 | 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 | 107908 | m = getMask(pMaskSet, sWBI.pSrc->iCursor); |
| 107545 | 107909 | if( (m & sWBI.notValid)==0 ){ |
| 107546 | 107910 | if( j==iFrom ) iFrom++; |
| 107547 | 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; | |
| 107548 | 107934 | } |
| 107549 | 107935 | sWBI.notReady = (isOptimal ? m : sWBI.notValid); |
| 107550 | 107936 | if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; |
| 107551 | 107937 | |
| 107552 | 107938 | WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", |
| @@ -107572,12 +107958,12 @@ | ||
| 107572 | 107958 | if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| 107573 | 107959 | notIndexed |= m; |
| 107574 | 107960 | } |
| 107575 | 107961 | if( isOptimal ){ |
| 107576 | 107962 | 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 | |
| 107579 | 107965 | ** very different inner loop (optimal) cost, we want to choose |
| 107580 | 107966 | ** for the outer loop that table which benefits the least from |
| 107581 | 107967 | ** being in the inner loop. The following code scales the |
| 107582 | 107968 | ** outer loop cost estimate to accomplish that. */ |
| 107583 | 107969 | WHERETRACE((" scaling cost from %.1f to %.1f\n", |
| @@ -107618,15 +108004,23 @@ | ||
| 107618 | 108004 | sWBI.cost.rCost, sWBI.cost.plan.nRow, |
| 107619 | 108005 | sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); |
| 107620 | 108006 | bestPlan = sWBI.cost; |
| 107621 | 108007 | bestJ = j; |
| 107622 | 108008 | } |
| 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; | |
| 107624 | 108014 | } |
| 107625 | 108015 | } |
| 107626 | 108016 | assert( bestJ>=0 ); |
| 107627 | 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 ); | |
| 107628 | 108022 | WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" |
| 107629 | 108023 | " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", |
| 107630 | 108024 | bestJ, pTabList->a[bestJ].pTab->zName, |
| 107631 | 108025 | pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, |
| 107632 | 108026 | bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); |
| @@ -108182,10 +108576,11 @@ | ||
| 108182 | 108576 | Expr* yy122; |
| 108183 | 108577 | Select* yy159; |
| 108184 | 108578 | IdList* yy180; |
| 108185 | 108579 | struct {int value; int mask;} yy207; |
| 108186 | 108580 | u8 yy258; |
| 108581 | + u16 yy305; | |
| 108187 | 108582 | struct LikeOp yy318; |
| 108188 | 108583 | TriggerStep* yy327; |
| 108189 | 108584 | ExprSpan yy342; |
| 108190 | 108585 | SrcList* yy347; |
| 108191 | 108586 | int yy392; |
| @@ -110132,22 +110527,19 @@ | ||
| 110132 | 110527 | case 82: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ yytestcase(yyruleno==82); |
| 110133 | 110528 | case 84: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==84); |
| 110134 | 110529 | case 86: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ yytestcase(yyruleno==86); |
| 110135 | 110530 | case 98: /* defer_subclause_opt ::= */ yytestcase(yyruleno==98); |
| 110136 | 110531 | case 109: /* ifexists ::= */ yytestcase(yyruleno==109); |
| 110137 | - case 120: /* distinct ::= ALL */ yytestcase(yyruleno==120); | |
| 110138 | - case 121: /* distinct ::= */ yytestcase(yyruleno==121); | |
| 110139 | 110532 | case 221: /* between_op ::= BETWEEN */ yytestcase(yyruleno==221); |
| 110140 | 110533 | case 224: /* in_op ::= IN */ yytestcase(yyruleno==224); |
| 110141 | 110534 | {yygotominor.yy392 = 0;} |
| 110142 | 110535 | break; |
| 110143 | 110536 | case 29: /* ifnotexists ::= IF NOT EXISTS */ |
| 110144 | 110537 | case 30: /* temp ::= TEMP */ yytestcase(yyruleno==30); |
| 110145 | 110538 | case 70: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==70); |
| 110146 | 110539 | case 85: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ yytestcase(yyruleno==85); |
| 110147 | 110540 | case 108: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==108); |
| 110148 | - case 119: /* distinct ::= DISTINCT */ yytestcase(yyruleno==119); | |
| 110149 | 110541 | case 222: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==222); |
| 110150 | 110542 | case 225: /* in_op ::= NOT IN */ yytestcase(yyruleno==225); |
| 110151 | 110543 | {yygotominor.yy392 = 1;} |
| 110152 | 110544 | break; |
| 110153 | 110545 | case 32: /* create_table_args ::= LP columnlist conslist_opt RP */ |
| @@ -110383,12 +110775,19 @@ | ||
| 110383 | 110775 | case 116: /* multiselect_op ::= UNION ALL */ |
| 110384 | 110776 | {yygotominor.yy392 = TK_ALL;} |
| 110385 | 110777 | break; |
| 110386 | 110778 | case 118: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ |
| 110387 | 110779 | { |
| 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); | |
| 110389 | 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;} | |
| 110390 | 110789 | break; |
| 110391 | 110790 | case 122: /* sclp ::= selcollist COMMA */ |
| 110392 | 110791 | case 246: /* idxlist_opt ::= LP idxlist RP */ yytestcase(yyruleno==246); |
| 110393 | 110792 | {yygotominor.yy442 = yymsp[-1].minor.yy442;} |
| 110394 | 110793 | break; |
| @@ -110454,14 +110853,24 @@ | ||
| 110454 | 110853 | break; |
| 110455 | 110854 | case 136: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ |
| 110456 | 110855 | { |
| 110457 | 110856 | if( yymsp[-6].minor.yy347==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy122==0 && yymsp[0].minor.yy180==0 ){ |
| 110458 | 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); | |
| 110459 | 110868 | }else{ |
| 110460 | 110869 | Select *pSubquery; |
| 110461 | 110870 | 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); | |
| 110463 | 110872 | yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy122,yymsp[0].minor.yy180); |
| 110464 | 110873 | } |
| 110465 | 110874 | } |
| 110466 | 110875 | break; |
| 110467 | 110876 | case 137: /* dbnm ::= */ |
| @@ -110690,11 +111099,11 @@ | ||
| 110690 | 111099 | if( yymsp[-1].minor.yy442 && yymsp[-1].minor.yy442->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ |
| 110691 | 111100 | sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0); |
| 110692 | 111101 | } |
| 110693 | 111102 | yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy442, &yymsp[-4].minor.yy0); |
| 110694 | 111103 | 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 ){ | |
| 110696 | 111105 | yygotominor.yy342.pExpr->flags |= EP_Distinct; |
| 110697 | 111106 | } |
| 110698 | 111107 | } |
| 110699 | 111108 | break; |
| 110700 | 111109 | case 197: /* expr ::= ID LP STAR RP */ |
| @@ -136337,11 +136746,12 @@ | ||
| 136337 | 136746 | ** would fit in a single node, use a smaller node-size. |
| 136338 | 136747 | */ |
| 136339 | 136748 | static int getNodeSize( |
| 136340 | 136749 | sqlite3 *db, /* Database handle */ |
| 136341 | 136750 | 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 */ | |
| 136343 | 136753 | ){ |
| 136344 | 136754 | int rc; |
| 136345 | 136755 | char *zSql; |
| 136346 | 136756 | if( isCreate ){ |
| 136347 | 136757 | int iPageSize = 0; |
| @@ -136350,17 +136760,22 @@ | ||
| 136350 | 136760 | if( rc==SQLITE_OK ){ |
| 136351 | 136761 | pRtree->iNodeSize = iPageSize-64; |
| 136352 | 136762 | if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){ |
| 136353 | 136763 | pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; |
| 136354 | 136764 | } |
| 136765 | + }else{ | |
| 136766 | + *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); | |
| 136355 | 136767 | } |
| 136356 | 136768 | }else{ |
| 136357 | 136769 | zSql = sqlite3_mprintf( |
| 136358 | 136770 | "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", |
| 136359 | 136771 | pRtree->zDb, pRtree->zName |
| 136360 | 136772 | ); |
| 136361 | 136773 | rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); |
| 136774 | + if( rc!=SQLITE_OK ){ | |
| 136775 | + *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); | |
| 136776 | + } | |
| 136362 | 136777 | } |
| 136363 | 136778 | |
| 136364 | 136779 | sqlite3_free(zSql); |
| 136365 | 136780 | return rc; |
| 136366 | 136781 | } |
| @@ -136420,11 +136835,11 @@ | ||
| 136420 | 136835 | pRtree->eCoordType = eCoordType; |
| 136421 | 136836 | memcpy(pRtree->zDb, argv[1], nDb); |
| 136422 | 136837 | memcpy(pRtree->zName, argv[2], nName); |
| 136423 | 136838 | |
| 136424 | 136839 | /* Figure out the node size to use. */ |
| 136425 | - rc = getNodeSize(db, pRtree, isCreate); | |
| 136840 | + rc = getNodeSize(db, pRtree, isCreate, pzErr); | |
| 136426 | 136841 | |
| 136427 | 136842 | /* Create/Connect to the underlying relational database schema. If |
| 136428 | 136843 | ** that is successful, call sqlite3_declare_vtab() to configure |
| 136429 | 136844 | ** the r-tree table schema. |
| 136430 | 136845 | */ |
| 136431 | 136846 |
| --- 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 @@ | ||
| 105 | 105 | ** |
| 106 | 106 | ** See also: [sqlite3_libversion()], |
| 107 | 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | 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" | |
| 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 | 113 | |
| 114 | 114 | /* |
| 115 | 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | 117 | ** |
| @@ -1590,11 +1590,11 @@ | ||
| 1590 | 1590 | ** database connection is opened. By default, URI handling is globally |
| 1591 | 1591 | ** disabled. The default value may be changed by compiling with the |
| 1592 | 1592 | ** [SQLITE_USE_URI] symbol defined. |
| 1593 | 1593 | ** |
| 1594 | 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 | |
| 1595 | +** <dd> This option takes a single integer argument which is interpreted as | |
| 1596 | 1596 | ** a boolean in order to enable or disable the use of covering indices for |
| 1597 | 1597 | ** full table scans in the query optimizer. The default setting is determined |
| 1598 | 1598 | ** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on" |
| 1599 | 1599 | ** if that compile-time option is omitted. |
| 1600 | 1600 | ** The ability to disable the use of covering indices for full table scans |
| 1601 | 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.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 @@ | ||
| 66 | 66 | @ </td></tr> |
| 67 | 67 | if( !brief ){ |
| 68 | 68 | @ <tr><th>Number Of Artifacts:</th><td> |
| 69 | 69 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 70 | 70 | 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) | |
| 72 | 72 | @ </td></tr> |
| 73 | 73 | if( n>0 ){ |
| 74 | 74 | int a, b; |
| 75 | 75 | Stmt q; |
| 76 | 76 | @ <tr><th>Uncompressed Artifact Size:</th><td> |
| @@ -94,11 +94,11 @@ | ||
| 94 | 94 | a = t/fsize; |
| 95 | 95 | @ %d(a):%d(b) |
| 96 | 96 | @ </td></tr> |
| 97 | 97 | } |
| 98 | 98 | @ <tr><th>Number Of 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*/"); | |
| 100 | 100 | @ %d(n) |
| 101 | 101 | @ </td></tr> |
| 102 | 102 | @ <tr><th>Number Of Files:</th><td> |
| 103 | 103 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 104 | 104 | @ %d(n) |
| @@ -115,17 +115,16 @@ | ||
| 115 | 115 | @ </td></tr> |
| 116 | 116 | } |
| 117 | 117 | @ <tr><th>Duration Of Project:</th><td> |
| 118 | 118 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 119 | 119 | " + 0.99"); |
| 120 | - @ %d(n) days or approximately %.2f(n/356.24) years. | |
| 120 | + @ %d(n) days or approximately %.2f(n/365.2425) years. | |
| 121 | 121 | @ </td></tr> |
| 122 | 122 | @ <tr><th>Project ID:</th><td>%h(db_get("project-code",""))</td></tr> |
| 123 | - @ <tr><th>Server ID:</th><td>%h(db_get("server-code",""))</td></tr> | |
| 124 | 123 | @ <tr><th>Fossil 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)] | |
| 127 | 126 | @ </td></tr> |
| 128 | 127 | @ <tr><th>SQLite Version:</th><td>%.19s(SQLITE_SOURCE_ID) |
| 129 | 128 | @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr> |
| 130 | 129 | @ <tr><th>Database Stats:</th><td> |
| 131 | 130 | zDb = db_name("repository"); |
| @@ -137,10 +136,102 @@ | ||
| 137 | 136 | @ </td></tr> |
| 138 | 137 | |
| 139 | 138 | @ </table> |
| 140 | 139 | style_footer(); |
| 141 | 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 | + | |
| 142 | 233 | |
| 143 | 234 | /* |
| 144 | 235 | ** WEBPAGE: urllist |
| 145 | 236 | ** |
| 146 | 237 | ** Show ways in which this repository has been accessed |
| 147 | 238 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -66,11 +66,11 @@ | |
| 66 | @ </td></tr> |
| 67 | if( !brief ){ |
| 68 | @ <tr><th>Number Of 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 Artifact Size:</th><td> |
| @@ -94,11 +94,11 @@ | |
| 94 | a = t/fsize; |
| 95 | @ %d(a):%d(b) |
| 96 | @ </td></tr> |
| 97 | } |
| 98 | @ <tr><th>Number Of 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 Of 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 Of 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 ID:</th><td>%h(db_get("project-code",""))</td></tr> |
| 123 | @ <tr><th>Server ID:</th><td>%h(db_get("server-code",""))</td></tr> |
| 124 | @ <tr><th>Fossil Version:</th><td> |
| 125 | @ %h(RELEASE_VERSION) %h(MANIFEST_DATE) %h(MANIFEST_VERSION) |
| 126 | @ (%h(COMPILER_NAME)) |
| 127 | @ </td></tr> |
| 128 | @ <tr><th>SQLite Version:</th><td>%.19s(SQLITE_SOURCE_ID) |
| 129 | @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr> |
| 130 | @ <tr><th>Database 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 Of 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 Artifact Size:</th><td> |
| @@ -94,11 +94,11 @@ | |
| 94 | a = t/fsize; |
| 95 | @ %d(a):%d(b) |
| 96 | @ </td></tr> |
| 97 | } |
| 98 | @ <tr><th>Number Of 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 Of 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 Of 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 ID:</th><td>%h(db_get("project-code",""))</td></tr> |
| 123 | @ <tr><th>Fossil 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 Version:</th><td>%.19s(SQLITE_SOURCE_ID) |
| 128 | @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr> |
| 129 | @ <tr><th>Database 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 @@ | ||
| 439 | 439 | /* |
| 440 | 440 | ** The default page footer |
| 441 | 441 | */ |
| 442 | 442 | const char zDefaultFooter[] = |
| 443 | 443 | @ <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 | |
| 445 | 447 | @ </div> |
| 446 | 448 | @ </body></html> |
| 447 | 449 | ; |
| 448 | 450 | |
| 449 | 451 | /* |
| @@ -586,12 +588,13 @@ | ||
| 586 | 588 | @ div.footer a:visited { color: white; } |
| 587 | 589 | @ div.footer a:hover { background-color: white; color: #558195; } |
| 588 | 590 | @ |
| 589 | 591 | @ /* verbatim blocks */ |
| 590 | 592 | @ pre.verbatim { |
| 591 | -@ background-color: #f5f5f5; | |
| 592 | -@ padding: 0.5em; | |
| 593 | +@ background-color: #f5f5f5; | |
| 594 | +@ padding: 0.5em; | |
| 595 | +@ white-space: pre-wrap; | |
| 593 | 596 | @} |
| 594 | 597 | @ |
| 595 | 598 | @ /* The label/value pairs on (for example) the ci page */ |
| 596 | 599 | @ table.label-value th { |
| 597 | 600 | @ vertical-align: top; |
| @@ -959,11 +962,11 @@ | ||
| 959 | 962 | @ line-height: 100%; |
| 960 | 963 | }, |
| 961 | 964 | { "div.sbsdiff", |
| 962 | 965 | "side-by-side diff display", |
| 963 | 966 | @ font-family: monospace; |
| 964 | - @ font-size: smaller; | |
| 967 | + @ font-size: xx-small; | |
| 965 | 968 | @ white-space: pre; |
| 966 | 969 | }, |
| 967 | 970 | { "div.udiff", |
| 968 | 971 | "context diff display", |
| 969 | 972 | @ font-family: monospace; |
| @@ -991,10 +994,21 @@ | ||
| 991 | 994 | }, |
| 992 | 995 | { "span.modpending", |
| 993 | 996 | "Moderation Pending message on timeline", |
| 994 | 997 | @ color: #b03800; |
| 995 | 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; | |
| 996 | 1010 | }, |
| 997 | 1011 | { 0, |
| 998 | 1012 | 0, |
| 999 | 1013 | 0 |
| 1000 | 1014 | } |
| 1001 | 1015 |
| --- 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 @@ | ||
| 211 | 211 | if( tmFlags & TIMELINE_GRAPH ){ |
| 212 | 212 | pGraph = graph_init(); |
| 213 | 213 | /* style is not moved to css, because this is |
| 214 | 214 | ** a technical div for the timeline graph |
| 215 | 215 | */ |
| 216 | - @ <div id="canvas" style="position:relative;width:1px;height:1px;" | |
| 216 | + @ <div id="canvas" style="position:relative;height:0px;width:0px;" | |
| 217 | 217 | @ onclick="clickOnGraph(event)"></div> |
| 218 | 218 | } |
| 219 | 219 | db_static_prepare(&qbranch, |
| 220 | 220 | "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", |
| 221 | 221 | TAG_BRANCH |
| 222 | 222 | ); |
| 223 | 223 | |
| 224 | - @ <table id="timelineTable" class="timelineTable"> | |
| 224 | + @ <table id="timelineTable" class="timelineTable" | |
| 225 | + @ onclick="clickOnGraph(event)"> | |
| 225 | 226 | blob_zero(&comment); |
| 226 | 227 | while( db_step(pQuery)==SQLITE_ROW ){ |
| 227 | 228 | int rid = db_column_int(pQuery, 0); |
| 228 | 229 | const char *zUuid = db_column_text(pQuery, 1); |
| 229 | 230 | int isLeaf = db_column_int(pQuery, 5); |
| @@ -426,10 +427,12 @@ | ||
| 426 | 427 | " (SELECT uuid FROM blob WHERE rid=fid)," |
| 427 | 428 | " (SELECT uuid FROM blob WHERE rid=pid)," |
| 428 | 429 | " (SELECT name FROM filename WHERE fnid=mlink.pfnid) AS oldnm" |
| 429 | 430 | " FROM mlink" |
| 430 | 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))" | |
| 431 | 434 | " ORDER BY 3 /*sort*/" |
| 432 | 435 | ); |
| 433 | 436 | fchngQueryInit = 1; |
| 434 | 437 | } |
| 435 | 438 | db_bind_int(&fchngQuery, ":mid", rid); |
| @@ -725,42 +728,16 @@ | ||
| 725 | 728 | @ for(var i in rowinfo){ |
| 726 | 729 | @ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY; |
| 727 | 730 | @ rowinfo[i].x = left + rowinfo[i].r*railPitch; |
| 728 | 731 | @ } |
| 729 | 732 | @ 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 | 733 | @ for(var i in rowinfo){ |
| 753 | 734 | @ drawNode(rowinfo[i], left, btm); |
| 754 | 735 | @ } |
| 755 | 736 | @ if( selRow!=null ) clickOnRow(selRow); |
| 756 | 737 | @ } |
| 757 | 738 | @ 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 | 739 | @ var x=event.clientX-absoluteX("canvas"); |
| 763 | 740 | @ var y=event.clientY-absoluteY("canvas"); |
| 764 | 741 | @ if(window.pageXOffset!=null){ |
| 765 | 742 | @ x += window.pageXOffset; |
| 766 | 743 | @ y += window.pageYOffset; |
| @@ -768,16 +745,18 @@ | ||
| 768 | 745 | @ var d = window.document.documentElement; |
| 769 | 746 | @ if(document.compatMode!="CSS1Compat") d = d.body; |
| 770 | 747 | @ x += d.scrollLeft; |
| 771 | 748 | @ y += d.scrollTop; |
| 772 | 749 | @ } |
| 773 | -#endif | |
| 750 | + if( P("clicktest")!=0 ){ | |
| 751 | + @ alert("click at "+x+","+y) | |
| 752 | + } | |
| 774 | 753 | @ for(var i in rowinfo){ |
| 775 | 754 | @ 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 ){ | |
| 779 | 758 | @ clickOnRow(p); |
| 780 | 759 | @ break; |
| 781 | 760 | @ } |
| 782 | 761 | @ } |
| 783 | 762 | @ } |
| @@ -1413,10 +1392,12 @@ | ||
| 1413 | 1392 | ** 1. uuid |
| 1414 | 1393 | ** 2. Date/Time |
| 1415 | 1394 | ** 3. Comment string and user |
| 1416 | 1395 | ** 4. Number of non-merge children |
| 1417 | 1396 | ** 5. Number of parents |
| 1397 | +** 6. mtime | |
| 1398 | +** 7. branch | |
| 1418 | 1399 | */ |
| 1419 | 1400 | void print_timeline(Stmt *q, int mxLine, int showfiles){ |
| 1420 | 1401 | int nLine = 0; |
| 1421 | 1402 | char zPrevDate[20]; |
| 1422 | 1403 | const char *zCurrentUuid=0; |
| @@ -1521,15 +1502,21 @@ | ||
| 1521 | 1502 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 1522 | 1503 | @ FROM tag, tagxref |
| 1523 | 1504 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 1524 | 1505 | @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) |
| 1525 | 1506 | @ || ')' 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, | |
| 1527 | 1509 | @ (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 | |
| 1530 | 1516 | @ WHERE blob.rid=event.objid |
| 1517 | + @ AND tag.tagname='branch' | |
| 1531 | 1518 | ; |
| 1532 | 1519 | return zBaseSql; |
| 1533 | 1520 | } |
| 1534 | 1521 | |
| 1535 | 1522 | /* |
| 1536 | 1523 |
| --- 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 @@ | ||
| 34 | 34 | char *zAppend; /* Value to append */ |
| 35 | 35 | unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */ |
| 36 | 36 | } *aField; |
| 37 | 37 | #define USEDBY_TICKET 01 |
| 38 | 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 */ | |
| 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 */ | |
| 41 | 44 | |
| 42 | 45 | /* |
| 43 | 46 | ** Compare two entries in aField[] for sorting purposes |
| 44 | 47 | */ |
| 45 | 48 | static int nameCmpr(const void *a, const void *b){ |
| @@ -74,11 +77,14 @@ | ||
| 74 | 77 | once = 1; |
| 75 | 78 | db_prepare(&q, "PRAGMA table_info(ticket)"); |
| 76 | 79 | while( db_step(&q)==SQLITE_ROW ){ |
| 77 | 80 | const char *zFieldName = db_column_text(&q, 1); |
| 78 | 81 | 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 | + } | |
| 80 | 86 | if( nField%10==0 ){ |
| 81 | 87 | aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); |
| 82 | 88 | } |
| 83 | 89 | aField[nField].zName = mprintf("%s", zFieldName); |
| 84 | 90 | aField[nField].mUsed = USEDBY_TICKET; |
| @@ -87,11 +93,14 @@ | ||
| 87 | 93 | db_finalize(&q); |
| 88 | 94 | db_prepare(&q, "PRAGMA table_info(ticketchng)"); |
| 89 | 95 | while( db_step(&q)==SQLITE_ROW ){ |
| 90 | 96 | const char *zFieldName = db_column_text(&q, 1); |
| 91 | 97 | 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 | + } | |
| 93 | 102 | if( (i = fieldId(zFieldName))>=0 ){ |
| 94 | 103 | aField[i].mUsed |= USEDBY_TICKETCHNG; |
| 95 | 104 | continue; |
| 96 | 105 | } |
| 97 | 106 | if( nField%10==0 ){ |
| @@ -183,10 +192,11 @@ | ||
| 183 | 192 | */ |
| 184 | 193 | static int ticket_insert(const Manifest *p, int rid, int tktid){ |
| 185 | 194 | Blob sql1, sql2, sql3; |
| 186 | 195 | Stmt q; |
| 187 | 196 | int i, j; |
| 197 | + char *aUsed; | |
| 188 | 198 | |
| 189 | 199 | if( tktid==0 ){ |
| 190 | 200 | db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " |
| 191 | 201 | "VALUES(%Q, 0)", p->zTicketUuid); |
| 192 | 202 | tktid = db_last_insert_rowid(); |
| @@ -193,22 +203,25 @@ | ||
| 193 | 203 | } |
| 194 | 204 | blob_zero(&sql1); |
| 195 | 205 | blob_zero(&sql2); |
| 196 | 206 | blob_zero(&sql3); |
| 197 | 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); | |
| 198 | 213 | for(i=0; i<p->nField; i++){ |
| 199 | 214 | 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++; | |
| 204 | 220 | blob_appendf(&sql1,", %s=coalesce(%s,'') || %Q", |
| 205 | 221 | 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{ | |
| 210 | 223 | blob_appendf(&sql1,", %s=%Q", zName, p->aField[i].zValue); |
| 211 | 224 | } |
| 212 | 225 | } |
| 213 | 226 | if( aField[j].mUsed & USEDBY_TICKETCHNG ){ |
| 214 | 227 | blob_appendf(&sql2, ",%s", zName); |
| @@ -222,20 +235,41 @@ | ||
| 222 | 235 | db_prepare(&q, "%s", blob_str(&sql1)); |
| 223 | 236 | db_bind_double(&q, ":mtime", p->rDate); |
| 224 | 237 | db_step(&q); |
| 225 | 238 | db_finalize(&q); |
| 226 | 239 | 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 | + } | |
| 231 | 264 | db_bind_double(&q, ":mtime", p->rDate); |
| 232 | 265 | db_step(&q); |
| 233 | 266 | db_finalize(&q); |
| 234 | 267 | } |
| 235 | 268 | blob_reset(&sql2); |
| 236 | 269 | blob_reset(&sql3); |
| 270 | + fossil_free(aUsed); | |
| 237 | 271 | return tktid; |
| 238 | 272 | } |
| 239 | 273 | |
| 240 | 274 | /* |
| 241 | 275 | ** Rebuild an entire entry in the TICKET table |
| @@ -268,10 +302,11 @@ | ||
| 268 | 302 | } |
| 269 | 303 | createFlag = 0; |
| 270 | 304 | } |
| 271 | 305 | db_finalize(&q); |
| 272 | 306 | } |
| 307 | + | |
| 273 | 308 | |
| 274 | 309 | /* |
| 275 | 310 | ** Create the TH1 interpreter and load the "common" code. |
| 276 | 311 | */ |
| 277 | 312 | void ticket_init(void){ |
| @@ -328,10 +363,32 @@ | ||
| 328 | 363 | ticket_rebuild_entry(zName); |
| 329 | 364 | } |
| 330 | 365 | db_finalize(&q); |
| 331 | 366 | db_end_transaction(0); |
| 332 | 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 | +} | |
| 333 | 390 | |
| 334 | 391 | /* |
| 335 | 392 | ** For trouble-shooting purposes, render a dump of the aField[] table to |
| 336 | 393 | ** the webpage currently under construction. |
| 337 | 394 | */ |
| 338 | 395 |
| --- 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 |
+2
| --- src/tktsetup.c | ||
| +++ src/tktsetup.c | ||
| @@ -67,10 +67,11 @@ | ||
| 67 | 67 | @ CREATE TABLE ticket( |
| 68 | 68 | @ -- Do not change any column that begins with tkt_ |
| 69 | 69 | @ tkt_id INTEGER PRIMARY KEY, |
| 70 | 70 | @ tkt_uuid TEXT UNIQUE, |
| 71 | 71 | @ tkt_mtime DATE, |
| 72 | +@ tkt_ctime DATE, | |
| 72 | 73 | @ -- Add as many fields as required below this line |
| 73 | 74 | @ type TEXT, |
| 74 | 75 | @ status TEXT, |
| 75 | 76 | @ subsystem TEXT, |
| 76 | 77 | @ priority TEXT, |
| @@ -82,10 +83,11 @@ | ||
| 82 | 83 | @ comment TEXT |
| 83 | 84 | @ ); |
| 84 | 85 | @ CREATE TABLE ticketchng( |
| 85 | 86 | @ -- Do not change any column that begins with tkt_ |
| 86 | 87 | @ tkt_id INTEGER REFERENCES ticket, |
| 88 | +@ tkt_rid INTEGER REFERENCES blob, | |
| 87 | 89 | @ tkt_mtime DATE, |
| 88 | 90 | @ -- Add as many fields as required below this line |
| 89 | 91 | @ login TEXT, |
| 90 | 92 | @ username TEXT, |
| 91 | 93 | @ mimetype TEXT, |
| 92 | 94 | |
| 93 | 95 | 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 @@ | ||
| 715 | 715 | int vid; |
| 716 | 716 | vid = db_lget_int("checkout", 0); |
| 717 | 717 | vfile_check_signature(vid, 0); |
| 718 | 718 | db_multi_exec( |
| 719 | 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;" | |
| 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;" | |
| 724 | 728 | ); |
| 725 | 729 | } |
| 726 | 730 | blob_zero(&record); |
| 727 | 731 | db_prepare(&q, "SELECT name FROM torevert"); |
| 728 | 732 | if( zRevision==0 ){ |
| @@ -736,11 +740,12 @@ | ||
| 736 | 740 | zFile = db_column_text(&q, 0); |
| 737 | 741 | zFull = mprintf("%/%/", g.zLocalRoot, zFile); |
| 738 | 742 | errCode = historical_version_of_file(zRevision, zFile, &record, |
| 739 | 743 | &isLink, &isExe, 0, 2); |
| 740 | 744 | 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 ){ | |
| 742 | 747 | fossil_print("UNMANAGE: %s\n", zFile); |
| 743 | 748 | }else{ |
| 744 | 749 | undo_save(zFile); |
| 745 | 750 | file_delete(zFull); |
| 746 | 751 | fossil_print("DELETE: %s\n", zFile); |
| 747 | 752 |
| --- 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 @@ | ||
| 303 | 303 | ** |
| 304 | 304 | ** (2) If the local database is open, check in VVAR. |
| 305 | 305 | ** |
| 306 | 306 | ** (3) Check the default user in the repository |
| 307 | 307 | ** |
| 308 | -** (4) Try the USER environment variable. | |
| 308 | +** (4) Try the FOSSIL_USER environment variable. | |
| 309 | +** | |
| 310 | +** (5) Try the USER environment variable. | |
| 309 | 311 | ** |
| 310 | -** (5) Try the USERNAME environment variable. | |
| 312 | +** (6) Try the USERNAME environment variable. | |
| 311 | 313 | ** |
| 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. | |
| 313 | 315 | ** |
| 314 | 316 | ** The user name is stored in g.zLogin. The uid is in g.userUid. |
| 315 | 317 | */ |
| 316 | 318 | void user_select(void){ |
| 317 | 319 | char *zUrl; |
| @@ -326,10 +328,12 @@ | ||
| 326 | 328 | } |
| 327 | 329 | |
| 328 | 330 | if( g.localOpen && attempt_user(db_lget("default-user",0)) ) return; |
| 329 | 331 | |
| 330 | 332 | if( attempt_user(db_get("default-user", 0)) ) return; |
| 333 | + | |
| 334 | + if( attempt_user(fossil_getenv("FOSSIL_USER")) ) return; | |
| 331 | 335 | |
| 332 | 336 | if( attempt_user(fossil_getenv("USER")) ) return; |
| 333 | 337 | |
| 334 | 338 | if( attempt_user(fossil_getenv("USERNAME")) ) return; |
| 335 | 339 | |
| 336 | 340 |
| --- 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 @@ | ||
| 667 | 667 | if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){ |
| 668 | 668 | blob_init(&w2, pW2->zWiki, -1); |
| 669 | 669 | } |
| 670 | 670 | blob_zero(&d); |
| 671 | 671 | 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); | |
| 673 | 673 | @ <div class="udiff"> |
| 674 | 674 | @ %s(blob_str(&d)) |
| 675 | 675 | @ </div> |
| 676 | 676 | manifest_destroy(pW1); |
| 677 | 677 | manifest_destroy(pW2); |
| 678 | 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, 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 |
+23
| --- test/diff-test-1.wiki | ||
| +++ test/diff-test-1.wiki | ||
| @@ -18,10 +18,33 @@ | ||
| 18 | 18 | * <a href="../../../fdiff?v1=57b0d8183cab0e3d&v2=37b3ef49d73cdfe6" |
| 19 | 19 | target="testwindow">Large diff of sqlite3.c</a>. This diff was very |
| 20 | 20 | slow prior to the preformance enhancement change [9e15437e97]. |
| 21 | 21 | * <a href="../../../info/bda00cbada#chunk42" target="testwindow"> |
| 22 | 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> | |
| 23 | 38 | |
| 24 | 39 | External: |
| 25 | 40 | |
| 26 | 41 | * <a href="http://www.sqlite.org/src/fdiff?v1=aafcb21a74e41f9a&v2=a6d127dd05daf0f9#chunk3" target="testwindow"> |
| 27 | 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> | |
| 28 | 51 |
| --- 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 @@ | ||
| 26 | 26 | TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) |
| 27 | 27 | LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 |
| 28 | 28 | |
| 29 | 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 | 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 | |
| 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 | 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 | |
| 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 | 34 | |
| 35 | 35 | |
| 36 | 36 | RC=$(DMDIR)\bin\rcc |
| 37 | 37 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 38 | 38 | |
| @@ -46,11 +46,11 @@ | ||
| 46 | 46 | |
| 47 | 47 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 48 | 48 | $(RC) $(RCFLAGS) -o$@ $** |
| 49 | 49 | |
| 50 | 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 > $@ | |
| 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 | 52 | +echo fossil >> $@ |
| 53 | 53 | +echo fossil >> $@ |
| 54 | 54 | +echo $(LIBS) >> $@ |
| 55 | 55 | +echo. >> $@ |
| 56 | 56 | +echo fossil >> $@ |
| @@ -523,10 +523,16 @@ | ||
| 523 | 523 | $(OBJDIR)\rebuild$O : rebuild_.c rebuild.h |
| 524 | 524 | $(TCC) -o$@ -c rebuild_.c |
| 525 | 525 | |
| 526 | 526 | rebuild_.c : $(SRCDIR)\rebuild.c |
| 527 | 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 $** > $@ | |
| 528 | 534 | |
| 529 | 535 | $(OBJDIR)\report$O : report_.c report.h |
| 530 | 536 | $(TCC) -o$@ -c report_.c |
| 531 | 537 | |
| 532 | 538 | report_.c : $(SRCDIR)\report.c |
| @@ -643,10 +649,16 @@ | ||
| 643 | 649 | $(OBJDIR)\undo$O : undo_.c undo.h |
| 644 | 650 | $(TCC) -o$@ -c undo_.c |
| 645 | 651 | |
| 646 | 652 | undo_.c : $(SRCDIR)\undo.c |
| 647 | 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 $** > $@ | |
| 648 | 660 | |
| 649 | 661 | $(OBJDIR)\update$O : update_.c update.h |
| 650 | 662 | $(TCC) -o$@ -c update_.c |
| 651 | 663 | |
| 652 | 664 | update_.c : $(SRCDIR)\update.c |
| @@ -723,7 +735,7 @@ | ||
| 723 | 735 | |
| 724 | 736 | zip_.c : $(SRCDIR)\zip.c |
| 725 | 737 | +translate$E $** > $@ |
| 726 | 738 | |
| 727 | 739 | 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 | |
| 729 | 741 | @copy /Y nul: headers |
| 730 | 742 |
| --- 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 |
+24
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -322,10 +322,11 @@ | ||
| 322 | 322 | $(SRCDIR)/pivot.c \ |
| 323 | 323 | $(SRCDIR)/popen.c \ |
| 324 | 324 | $(SRCDIR)/pqueue.c \ |
| 325 | 325 | $(SRCDIR)/printf.c \ |
| 326 | 326 | $(SRCDIR)/rebuild.c \ |
| 327 | + $(SRCDIR)/regexp.c \ | |
| 327 | 328 | $(SRCDIR)/report.c \ |
| 328 | 329 | $(SRCDIR)/rss.c \ |
| 329 | 330 | $(SRCDIR)/schema.c \ |
| 330 | 331 | $(SRCDIR)/search.c \ |
| 331 | 332 | $(SRCDIR)/setup.c \ |
| @@ -342,10 +343,11 @@ | ||
| 342 | 343 | $(SRCDIR)/th_main.c \ |
| 343 | 344 | $(SRCDIR)/timeline.c \ |
| 344 | 345 | $(SRCDIR)/tkt.c \ |
| 345 | 346 | $(SRCDIR)/tktsetup.c \ |
| 346 | 347 | $(SRCDIR)/undo.c \ |
| 348 | + $(SRCDIR)/unicode.c \ | |
| 347 | 349 | $(SRCDIR)/update.c \ |
| 348 | 350 | $(SRCDIR)/url.c \ |
| 349 | 351 | $(SRCDIR)/user.c \ |
| 350 | 352 | $(SRCDIR)/utf8.c \ |
| 351 | 353 | $(SRCDIR)/verify.c \ |
| @@ -426,10 +428,11 @@ | ||
| 426 | 428 | $(OBJDIR)/pivot_.c \ |
| 427 | 429 | $(OBJDIR)/popen_.c \ |
| 428 | 430 | $(OBJDIR)/pqueue_.c \ |
| 429 | 431 | $(OBJDIR)/printf_.c \ |
| 430 | 432 | $(OBJDIR)/rebuild_.c \ |
| 433 | + $(OBJDIR)/regexp_.c \ | |
| 431 | 434 | $(OBJDIR)/report_.c \ |
| 432 | 435 | $(OBJDIR)/rss_.c \ |
| 433 | 436 | $(OBJDIR)/schema_.c \ |
| 434 | 437 | $(OBJDIR)/search_.c \ |
| 435 | 438 | $(OBJDIR)/setup_.c \ |
| @@ -446,10 +449,11 @@ | ||
| 446 | 449 | $(OBJDIR)/th_main_.c \ |
| 447 | 450 | $(OBJDIR)/timeline_.c \ |
| 448 | 451 | $(OBJDIR)/tkt_.c \ |
| 449 | 452 | $(OBJDIR)/tktsetup_.c \ |
| 450 | 453 | $(OBJDIR)/undo_.c \ |
| 454 | + $(OBJDIR)/unicode_.c \ | |
| 451 | 455 | $(OBJDIR)/update_.c \ |
| 452 | 456 | $(OBJDIR)/url_.c \ |
| 453 | 457 | $(OBJDIR)/user_.c \ |
| 454 | 458 | $(OBJDIR)/utf8_.c \ |
| 455 | 459 | $(OBJDIR)/verify_.c \ |
| @@ -530,10 +534,11 @@ | ||
| 530 | 534 | $(OBJDIR)/pivot.o \ |
| 531 | 535 | $(OBJDIR)/popen.o \ |
| 532 | 536 | $(OBJDIR)/pqueue.o \ |
| 533 | 537 | $(OBJDIR)/printf.o \ |
| 534 | 538 | $(OBJDIR)/rebuild.o \ |
| 539 | + $(OBJDIR)/regexp.o \ | |
| 535 | 540 | $(OBJDIR)/report.o \ |
| 536 | 541 | $(OBJDIR)/rss.o \ |
| 537 | 542 | $(OBJDIR)/schema.o \ |
| 538 | 543 | $(OBJDIR)/search.o \ |
| 539 | 544 | $(OBJDIR)/setup.o \ |
| @@ -550,10 +555,11 @@ | ||
| 550 | 555 | $(OBJDIR)/th_main.o \ |
| 551 | 556 | $(OBJDIR)/timeline.o \ |
| 552 | 557 | $(OBJDIR)/tkt.o \ |
| 553 | 558 | $(OBJDIR)/tktsetup.o \ |
| 554 | 559 | $(OBJDIR)/undo.o \ |
| 560 | + $(OBJDIR)/unicode.o \ | |
| 555 | 561 | $(OBJDIR)/update.o \ |
| 556 | 562 | $(OBJDIR)/url.o \ |
| 557 | 563 | $(OBJDIR)/user.o \ |
| 558 | 564 | $(OBJDIR)/utf8.o \ |
| 559 | 565 | $(OBJDIR)/verify.o \ |
| @@ -747,10 +753,11 @@ | ||
| 747 | 753 | $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ |
| 748 | 754 | $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ |
| 749 | 755 | $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ |
| 750 | 756 | $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ |
| 751 | 757 | $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ |
| 758 | + $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ | |
| 752 | 759 | $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ |
| 753 | 760 | $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ |
| 754 | 761 | $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ |
| 755 | 762 | $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ |
| 756 | 763 | $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ |
| @@ -767,10 +774,11 @@ | ||
| 767 | 774 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 768 | 775 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 769 | 776 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 770 | 777 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 771 | 778 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| 779 | + $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \ | |
| 772 | 780 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ |
| 773 | 781 | $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ |
| 774 | 782 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 775 | 783 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 776 | 784 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| @@ -1340,10 +1348,18 @@ | ||
| 1340 | 1348 | |
| 1341 | 1349 | $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h |
| 1342 | 1350 | $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c |
| 1343 | 1351 | |
| 1344 | 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 | |
| 1345 | 1361 | |
| 1346 | 1362 | $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate |
| 1347 | 1363 | $(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c |
| 1348 | 1364 | |
| 1349 | 1365 | $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h |
| @@ -1500,10 +1516,18 @@ | ||
| 1500 | 1516 | |
| 1501 | 1517 | $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h |
| 1502 | 1518 | $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c |
| 1503 | 1519 | |
| 1504 | 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 | |
| 1505 | 1529 | |
| 1506 | 1530 | $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate |
| 1507 | 1531 | $(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c |
| 1508 | 1532 | |
| 1509 | 1533 | $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h |
| 1510 | 1534 |
| --- 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 @@ | ||
| 322 | 322 | $(SRCDIR)/pivot.c \ |
| 323 | 323 | $(SRCDIR)/popen.c \ |
| 324 | 324 | $(SRCDIR)/pqueue.c \ |
| 325 | 325 | $(SRCDIR)/printf.c \ |
| 326 | 326 | $(SRCDIR)/rebuild.c \ |
| 327 | + $(SRCDIR)/regexp.c \ | |
| 327 | 328 | $(SRCDIR)/report.c \ |
| 328 | 329 | $(SRCDIR)/rss.c \ |
| 329 | 330 | $(SRCDIR)/schema.c \ |
| 330 | 331 | $(SRCDIR)/search.c \ |
| 331 | 332 | $(SRCDIR)/setup.c \ |
| @@ -342,10 +343,11 @@ | ||
| 342 | 343 | $(SRCDIR)/th_main.c \ |
| 343 | 344 | $(SRCDIR)/timeline.c \ |
| 344 | 345 | $(SRCDIR)/tkt.c \ |
| 345 | 346 | $(SRCDIR)/tktsetup.c \ |
| 346 | 347 | $(SRCDIR)/undo.c \ |
| 348 | + $(SRCDIR)/unicode.c \ | |
| 347 | 349 | $(SRCDIR)/update.c \ |
| 348 | 350 | $(SRCDIR)/url.c \ |
| 349 | 351 | $(SRCDIR)/user.c \ |
| 350 | 352 | $(SRCDIR)/utf8.c \ |
| 351 | 353 | $(SRCDIR)/verify.c \ |
| @@ -426,10 +428,11 @@ | ||
| 426 | 428 | $(OBJDIR)/pivot_.c \ |
| 427 | 429 | $(OBJDIR)/popen_.c \ |
| 428 | 430 | $(OBJDIR)/pqueue_.c \ |
| 429 | 431 | $(OBJDIR)/printf_.c \ |
| 430 | 432 | $(OBJDIR)/rebuild_.c \ |
| 433 | + $(OBJDIR)/regexp_.c \ | |
| 431 | 434 | $(OBJDIR)/report_.c \ |
| 432 | 435 | $(OBJDIR)/rss_.c \ |
| 433 | 436 | $(OBJDIR)/schema_.c \ |
| 434 | 437 | $(OBJDIR)/search_.c \ |
| 435 | 438 | $(OBJDIR)/setup_.c \ |
| @@ -446,10 +449,11 @@ | ||
| 446 | 449 | $(OBJDIR)/th_main_.c \ |
| 447 | 450 | $(OBJDIR)/timeline_.c \ |
| 448 | 451 | $(OBJDIR)/tkt_.c \ |
| 449 | 452 | $(OBJDIR)/tktsetup_.c \ |
| 450 | 453 | $(OBJDIR)/undo_.c \ |
| 454 | + $(OBJDIR)/unicode_.c \ | |
| 451 | 455 | $(OBJDIR)/update_.c \ |
| 452 | 456 | $(OBJDIR)/url_.c \ |
| 453 | 457 | $(OBJDIR)/user_.c \ |
| 454 | 458 | $(OBJDIR)/utf8_.c \ |
| 455 | 459 | $(OBJDIR)/verify_.c \ |
| @@ -530,10 +534,11 @@ | ||
| 530 | 534 | $(OBJDIR)/pivot.o \ |
| 531 | 535 | $(OBJDIR)/popen.o \ |
| 532 | 536 | $(OBJDIR)/pqueue.o \ |
| 533 | 537 | $(OBJDIR)/printf.o \ |
| 534 | 538 | $(OBJDIR)/rebuild.o \ |
| 539 | + $(OBJDIR)/regexp.o \ | |
| 535 | 540 | $(OBJDIR)/report.o \ |
| 536 | 541 | $(OBJDIR)/rss.o \ |
| 537 | 542 | $(OBJDIR)/schema.o \ |
| 538 | 543 | $(OBJDIR)/search.o \ |
| 539 | 544 | $(OBJDIR)/setup.o \ |
| @@ -550,10 +555,11 @@ | ||
| 550 | 555 | $(OBJDIR)/th_main.o \ |
| 551 | 556 | $(OBJDIR)/timeline.o \ |
| 552 | 557 | $(OBJDIR)/tkt.o \ |
| 553 | 558 | $(OBJDIR)/tktsetup.o \ |
| 554 | 559 | $(OBJDIR)/undo.o \ |
| 560 | + $(OBJDIR)/unicode.o \ | |
| 555 | 561 | $(OBJDIR)/update.o \ |
| 556 | 562 | $(OBJDIR)/url.o \ |
| 557 | 563 | $(OBJDIR)/user.o \ |
| 558 | 564 | $(OBJDIR)/utf8.o \ |
| 559 | 565 | $(OBJDIR)/verify.o \ |
| @@ -747,10 +753,11 @@ | ||
| 747 | 753 | $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ |
| 748 | 754 | $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ |
| 749 | 755 | $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ |
| 750 | 756 | $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ |
| 751 | 757 | $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ |
| 758 | + $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ | |
| 752 | 759 | $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ |
| 753 | 760 | $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ |
| 754 | 761 | $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ |
| 755 | 762 | $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ |
| 756 | 763 | $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ |
| @@ -767,10 +774,11 @@ | ||
| 767 | 774 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 768 | 775 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 769 | 776 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 770 | 777 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 771 | 778 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| 779 | + $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \ | |
| 772 | 780 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ |
| 773 | 781 | $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ |
| 774 | 782 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 775 | 783 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 776 | 784 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| @@ -1340,10 +1348,18 @@ | ||
| 1340 | 1348 | |
| 1341 | 1349 | $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h |
| 1342 | 1350 | $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c |
| 1343 | 1351 | |
| 1344 | 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 | |
| 1345 | 1361 | |
| 1346 | 1362 | $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate |
| 1347 | 1363 | $(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c |
| 1348 | 1364 | |
| 1349 | 1365 | $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h |
| @@ -1500,10 +1516,18 @@ | ||
| 1500 | 1516 | |
| 1501 | 1517 | $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h |
| 1502 | 1518 | $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c |
| 1503 | 1519 | |
| 1504 | 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 | |
| 1505 | 1529 | |
| 1506 | 1530 | $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate |
| 1507 | 1531 | $(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c |
| 1508 | 1532 | |
| 1509 | 1533 | $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h |
| 1510 | 1534 |
| --- 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 |
+20
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -107,10 +107,11 @@ | ||
| 107 | 107 | pivot_.c \ |
| 108 | 108 | popen_.c \ |
| 109 | 109 | pqueue_.c \ |
| 110 | 110 | printf_.c \ |
| 111 | 111 | rebuild_.c \ |
| 112 | + regexp_.c \ | |
| 112 | 113 | report_.c \ |
| 113 | 114 | rss_.c \ |
| 114 | 115 | schema_.c \ |
| 115 | 116 | search_.c \ |
| 116 | 117 | setup_.c \ |
| @@ -127,10 +128,11 @@ | ||
| 127 | 128 | th_main_.c \ |
| 128 | 129 | timeline_.c \ |
| 129 | 130 | tkt_.c \ |
| 130 | 131 | tktsetup_.c \ |
| 131 | 132 | undo_.c \ |
| 133 | + unicode_.c \ | |
| 132 | 134 | update_.c \ |
| 133 | 135 | url_.c \ |
| 134 | 136 | user_.c \ |
| 135 | 137 | utf8_.c \ |
| 136 | 138 | verify_.c \ |
| @@ -210,10 +212,11 @@ | ||
| 210 | 212 | $(OX)\pivot$O \ |
| 211 | 213 | $(OX)\popen$O \ |
| 212 | 214 | $(OX)\pqueue$O \ |
| 213 | 215 | $(OX)\printf$O \ |
| 214 | 216 | $(OX)\rebuild$O \ |
| 217 | + $(OX)\regexp$O \ | |
| 215 | 218 | $(OX)\report$O \ |
| 216 | 219 | $(OX)\rss$O \ |
| 217 | 220 | $(OX)\schema$O \ |
| 218 | 221 | $(OX)\search$O \ |
| 219 | 222 | $(OX)\setup$O \ |
| @@ -230,10 +233,11 @@ | ||
| 230 | 233 | $(OX)\th_main$O \ |
| 231 | 234 | $(OX)\timeline$O \ |
| 232 | 235 | $(OX)\tkt$O \ |
| 233 | 236 | $(OX)\tktsetup$O \ |
| 234 | 237 | $(OX)\undo$O \ |
| 238 | + $(OX)\unicode$O \ | |
| 235 | 239 | $(OX)\update$O \ |
| 236 | 240 | $(OX)\url$O \ |
| 237 | 241 | $(OX)\user$O \ |
| 238 | 242 | $(OX)\utf8$O \ |
| 239 | 243 | $(OX)\verify$O \ |
| @@ -330,10 +334,11 @@ | ||
| 330 | 334 | echo $(OX)\pivot.obj >> $@ |
| 331 | 335 | echo $(OX)\popen.obj >> $@ |
| 332 | 336 | echo $(OX)\pqueue.obj >> $@ |
| 333 | 337 | echo $(OX)\printf.obj >> $@ |
| 334 | 338 | echo $(OX)\rebuild.obj >> $@ |
| 339 | + echo $(OX)\regexp.obj >> $@ | |
| 335 | 340 | echo $(OX)\report.obj >> $@ |
| 336 | 341 | echo $(OX)\rss.obj >> $@ |
| 337 | 342 | echo $(OX)\schema.obj >> $@ |
| 338 | 343 | echo $(OX)\search.obj >> $@ |
| 339 | 344 | echo $(OX)\setup.obj >> $@ |
| @@ -354,10 +359,11 @@ | ||
| 354 | 359 | echo $(OX)\th_main.obj >> $@ |
| 355 | 360 | echo $(OX)\timeline.obj >> $@ |
| 356 | 361 | echo $(OX)\tkt.obj >> $@ |
| 357 | 362 | echo $(OX)\tktsetup.obj >> $@ |
| 358 | 363 | echo $(OX)\undo.obj >> $@ |
| 364 | + echo $(OX)\unicode.obj >> $@ | |
| 359 | 365 | echo $(OX)\update.obj >> $@ |
| 360 | 366 | echo $(OX)\url.obj >> $@ |
| 361 | 367 | echo $(OX)\user.obj >> $@ |
| 362 | 368 | echo $(OX)\utf8.obj >> $@ |
| 363 | 369 | echo $(OX)\verify.obj >> $@ |
| @@ -853,10 +859,16 @@ | ||
| 853 | 859 | $(OX)\rebuild$O : rebuild_.c rebuild.h |
| 854 | 860 | $(TCC) /Fo$@ -c rebuild_.c |
| 855 | 861 | |
| 856 | 862 | rebuild_.c : $(SRCDIR)\rebuild.c |
| 857 | 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 $** > $@ | |
| 858 | 870 | |
| 859 | 871 | $(OX)\report$O : report_.c report.h |
| 860 | 872 | $(TCC) /Fo$@ -c report_.c |
| 861 | 873 | |
| 862 | 874 | report_.c : $(SRCDIR)\report.c |
| @@ -973,10 +985,16 @@ | ||
| 973 | 985 | $(OX)\undo$O : undo_.c undo.h |
| 974 | 986 | $(TCC) /Fo$@ -c undo_.c |
| 975 | 987 | |
| 976 | 988 | undo_.c : $(SRCDIR)\undo.c |
| 977 | 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 $** > $@ | |
| 978 | 996 | |
| 979 | 997 | $(OX)\update$O : update_.c update.h |
| 980 | 998 | $(TCC) /Fo$@ -c update_.c |
| 981 | 999 | |
| 982 | 1000 | update_.c : $(SRCDIR)\update.c |
| @@ -1122,10 +1140,11 @@ | ||
| 1122 | 1140 | pivot_.c:pivot.h \ |
| 1123 | 1141 | popen_.c:popen.h \ |
| 1124 | 1142 | pqueue_.c:pqueue.h \ |
| 1125 | 1143 | printf_.c:printf.h \ |
| 1126 | 1144 | rebuild_.c:rebuild.h \ |
| 1145 | + regexp_.c:regexp.h \ | |
| 1127 | 1146 | report_.c:report.h \ |
| 1128 | 1147 | rss_.c:rss.h \ |
| 1129 | 1148 | schema_.c:schema.h \ |
| 1130 | 1149 | search_.c:search.h \ |
| 1131 | 1150 | setup_.c:setup.h \ |
| @@ -1142,10 +1161,11 @@ | ||
| 1142 | 1161 | th_main_.c:th_main.h \ |
| 1143 | 1162 | timeline_.c:timeline.h \ |
| 1144 | 1163 | tkt_.c:tkt.h \ |
| 1145 | 1164 | tktsetup_.c:tktsetup.h \ |
| 1146 | 1165 | undo_.c:undo.h \ |
| 1166 | + unicode_.c:unicode.h \ | |
| 1147 | 1167 | update_.c:update.h \ |
| 1148 | 1168 | url_.c:url.h \ |
| 1149 | 1169 | user_.c:user.h \ |
| 1150 | 1170 | utf8_.c:utf8.h \ |
| 1151 | 1171 | verify_.c:verify.h \ |
| 1152 | 1172 |
| --- 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 |
+3
| --- win/fossil.rc | ||
| +++ win/fossil.rc | ||
| @@ -104,10 +104,13 @@ | ||
| 104 | 104 | #endif |
| 105 | 105 | #endif |
| 106 | 106 | #ifdef FOSSIL_ENABLE_JSON |
| 107 | 107 | VALUE "JsonEnabled", "Yes, cson\0" |
| 108 | 108 | #endif |
| 109 | +#ifdef FOSSIL_ENABLE_MARKDOWN | |
| 110 | + VALUE "MarkdownEnabled", "Yes\0" | |
| 111 | +#endif | |
| 109 | 112 | END |
| 110 | 113 | END |
| 111 | 114 | BLOCK "VarFileInfo" |
| 112 | 115 | BEGIN |
| 113 | 116 | VALUE "Translation", 0x409, 0x4B0 |
| 114 | 117 |
| --- 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 @@ | ||
| 20 | 20 | * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| 21 | 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| 22 | 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| 23 | 23 | * OTHER DEALINGS IN THE SOFTWARE. |
| 24 | 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. | |
| 25 | 30 | * |
| 26 | 31 | * Version 1.12.1, Oct 1 2012, Toni Ronkko |
| 27 | 32 | * Bug fix: renamed wide-character DIR structure _wDIR to _WDIR (with |
| 28 | 33 | * capital W) in order to maintain compatibility with MingW. |
| 29 | 34 | * |
| 30 | 35 | * 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. | |
| 36 | 39 | * |
| 37 | 40 | * Do not include windows.h. This allows dirent.h to be integrated more |
| 38 | 41 | * easily into programs using winsock. Thanks to Fernando Azaldegui. |
| 39 | 42 | * |
| 40 | 43 | * Version 1.11, Mar 15, 2011, Toni Ronkko |
| @@ -88,48 +91,72 @@ | ||
| 88 | 91 | *****************************************************************************/ |
| 89 | 92 | #ifndef DIRENT_H |
| 90 | 93 | #define DIRENT_H |
| 91 | 94 | |
| 92 | 95 | #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86) |
| 93 | -#define _X86_ | |
| 96 | +# define _X86_ | |
| 94 | 97 | #endif |
| 95 | 98 | #include <stdio.h> |
| 96 | 99 | #include <stdarg.h> |
| 97 | 100 | #include <windef.h> |
| 98 | 101 | #include <winbase.h> |
| 99 | 102 | #include <wchar.h> |
| 100 | -#include <winnls.h> | |
| 101 | 103 | #include <string.h> |
| 102 | 104 | #include <stdlib.h> |
| 105 | +#include <malloc.h> | |
| 103 | 106 | #include <sys/types.h> |
| 104 | 107 | #include <sys/stat.h> |
| 105 | 108 | #include <errno.h> |
| 106 | 109 | |
| 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 | |
| 111 | 115 | |
| 112 | 116 | /* Entries missing from MSVC 6.0 */ |
| 113 | 117 | #if !defined(FILE_ATTRIBUTE_DEVICE) |
| 114 | -# define FILE_ATTRIBUTE_DEVICE 0x40 | |
| 118 | +# define FILE_ATTRIBUTE_DEVICE 0x40 | |
| 115 | 119 | #endif |
| 116 | 120 | |
| 117 | 121 | /* File type and permission flags for stat() */ |
| 118 | -#if defined(_MSC_VER) && !defined(S_IREAD) | |
| 122 | +#if !defined(S_IFMT) | |
| 119 | 123 | # define S_IFMT _S_IFMT /* File type mask */ |
| 124 | +#endif | |
| 125 | +#if !defined(S_IFDIR) | |
| 120 | 126 | # define S_IFDIR _S_IFDIR /* Directory */ |
| 127 | +#endif | |
| 128 | +#if !defined(S_IFCHR) | |
| 121 | 129 | # define S_IFCHR _S_IFCHR /* Character device */ |
| 130 | +#endif | |
| 131 | +#if !defined(S_IFFIFO) | |
| 122 | 132 | # define S_IFFIFO _S_IFFIFO /* Pipe */ |
| 133 | +#endif | |
| 134 | +#if !defined(S_IFREG) | |
| 123 | 135 | # define S_IFREG _S_IFREG /* Regular file */ |
| 136 | +#endif | |
| 137 | +#if !defined(S_IREAD) | |
| 124 | 138 | # define S_IREAD _S_IREAD /* Read permission */ |
| 139 | +#endif | |
| 140 | +#if !defined(S_IWRITE) | |
| 125 | 141 | # define S_IWRITE _S_IWRITE /* Write permission */ |
| 142 | +#endif | |
| 143 | +#if !defined(S_IEXEC) | |
| 126 | 144 | # define S_IEXEC _S_IEXEC /* Execute permission */ |
| 127 | 145 | #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 | |
| 131 | 158 | |
| 132 | 159 | #if defined(_MSC_VER) |
| 133 | 160 | # define S_IRUSR S_IREAD /* Read user */ |
| 134 | 161 | # define S_IWUSR S_IWRITE /* Write user */ |
| 135 | 162 | # define S_IXUSR 0 /* Execute user */ |
| @@ -139,18 +166,26 @@ | ||
| 139 | 166 | # define S_IROTH 0 /* Read others */ |
| 140 | 167 | # define S_IWOTH 0 /* Write others */ |
| 141 | 168 | # define S_IXOTH 0 /* Execute others */ |
| 142 | 169 | #endif |
| 143 | 170 | |
| 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 | |
| 146 | 181 | |
| 147 | 182 | /* File type flags for d_type */ |
| 148 | 183 | #define DT_UNKNOWN 0 |
| 149 | 184 | #define DT_REG S_IFREG |
| 150 | 185 | #define DT_DIR S_IFDIR |
| 151 | -#define DT_FIFO S_IFFIFO | |
| 186 | +#define DT_FIFO S_IFIFO | |
| 152 | 187 | #define DT_SOCK S_IFSOCK |
| 153 | 188 | #define DT_CHR S_IFCHR |
| 154 | 189 | #define DT_BLK S_IFBLK |
| 155 | 190 | |
| 156 | 191 | /* Macros for converting between st_mode and d_type */ |
| @@ -161,41 +196,31 @@ | ||
| 161 | 196 | * File type macros. Note that block devices, sockets and links cannot be |
| 162 | 197 | * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are |
| 163 | 198 | * only defined for compatibility. These macros should always return false |
| 164 | 199 | * on Windows. |
| 165 | 200 | */ |
| 166 | -#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO) | |
| 201 | +#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) | |
| 167 | 202 | #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) |
| 168 | 203 | #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) |
| 169 | 204 | #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) |
| 170 | 205 | #define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) |
| 171 | 206 | #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) |
| 172 | 207 | #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) |
| 173 | 208 | |
| 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 | + | |
| 191 | 215 | |
| 192 | 216 | #ifdef __cplusplus |
| 193 | 217 | extern "C" { |
| 194 | 218 | #endif |
| 195 | 219 | |
| 196 | -/* Wide-character versions */ | |
| 220 | + | |
| 221 | +/* Wide-character version */ | |
| 197 | 222 | struct _wdirent { |
| 198 | 223 | long d_ino; /* Always zero */ |
| 199 | 224 | unsigned short d_reclen; /* Structure size */ |
| 200 | 225 | size_t d_namlen; /* Length of name without \0 */ |
| 201 | 226 | int d_type; /* File type */ |
| @@ -203,11 +228,11 @@ | ||
| 203 | 228 | }; |
| 204 | 229 | typedef struct _wdirent _wdirent; |
| 205 | 230 | |
| 206 | 231 | struct _WDIR { |
| 207 | 232 | struct _wdirent ent; /* Current directory entry */ |
| 208 | - WIN32_FIND_DATAW find_data; /* Private file data */ | |
| 233 | + WIN32_FIND_DATAW data; /* Private file data */ | |
| 209 | 234 | int cached; /* True if data is valid */ |
| 210 | 235 | HANDLE handle; /* Win32 search handle */ |
| 211 | 236 | wchar_t *patt; /* Initial directory name */ |
| 212 | 237 | }; |
| 213 | 238 | typedef struct _WDIR _WDIR; |
| @@ -214,10 +239,11 @@ | ||
| 214 | 239 | |
| 215 | 240 | static _WDIR *_wopendir (const wchar_t *dirname); |
| 216 | 241 | static struct _wdirent *_wreaddir (_WDIR *dirp); |
| 217 | 242 | static int _wclosedir (_WDIR *dirp); |
| 218 | 243 | static void _wrewinddir (_WDIR* dirp); |
| 244 | + | |
| 219 | 245 | |
| 220 | 246 | /* For compatibility with Symbian */ |
| 221 | 247 | #define wdirent _wdirent |
| 222 | 248 | #define WDIR _WDIR |
| 223 | 249 | #define wopendir _wopendir |
| @@ -246,10 +272,30 @@ | ||
| 246 | 272 | static struct dirent *readdir (DIR *dirp); |
| 247 | 273 | static int closedir (DIR *dirp); |
| 248 | 274 | static void rewinddir (DIR* dirp); |
| 249 | 275 | |
| 250 | 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 | + | |
| 251 | 297 | /* |
| 252 | 298 | * Open directory stream DIRNAME for read and return a pointer to the |
| 253 | 299 | * internal working area that is used to retrieve individual directory |
| 254 | 300 | * entries. |
| 255 | 301 | */ |
| @@ -256,31 +302,38 @@ | ||
| 256 | 302 | static _WDIR* |
| 257 | 303 | _wopendir( |
| 258 | 304 | const wchar_t *dirname) |
| 259 | 305 | { |
| 260 | 306 | _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 | + } | |
| 262 | 314 | |
| 263 | 315 | /* Allocate new _WDIR structure */ |
| 264 | 316 | dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); |
| 265 | 317 | if (dirp != NULL) { |
| 266 | 318 | DWORD n; |
| 267 | 319 | |
| 268 | 320 | /* Reset _WDIR structure */ |
| 269 | 321 | dirp->handle = INVALID_HANDLE_VALUE; |
| 270 | 322 | dirp->patt = NULL; |
| 323 | + dirp->cached = 0; | |
| 271 | 324 | |
| 272 | 325 | /* Compute the length of full path plus zero terminator */ |
| 273 | 326 | n = GetFullPathNameW (dirname, 0, NULL, NULL); |
| 274 | 327 | |
| 275 | - /* Allocate room for full path and search patterns */ | |
| 328 | + /* Allocate room for absolute directory name and search pattern */ | |
| 276 | 329 | dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); |
| 277 | 330 | if (dirp->patt) { |
| 278 | 331 | |
| 279 | 332 | /* |
| 280 | 333 | * 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 | |
| 282 | 335 | * working directory is changed between opendir() and rewinddir(). |
| 283 | 336 | */ |
| 284 | 337 | n = GetFullPathNameW (dirname, n, dirp->patt, NULL); |
| 285 | 338 | if (n > 0) { |
| 286 | 339 | wchar_t *p; |
| @@ -303,25 +356,22 @@ | ||
| 303 | 356 | } |
| 304 | 357 | *p++ = '*'; |
| 305 | 358 | *p = '\0'; |
| 306 | 359 | |
| 307 | 360 | /* 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; | |
| 314 | 364 | } else { |
| 315 | - /* Search pattern is not a directory name? */ | |
| 316 | - DIRENT_SET_ERRNO (ENOENT); | |
| 365 | + /* Cannot retrieve first entry */ | |
| 317 | 366 | error = 1; |
| 367 | + dirent_set_errno (ENOENT); | |
| 318 | 368 | } |
| 319 | 369 | |
| 320 | 370 | } 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); | |
| 323 | 373 | error = 1; |
| 324 | 374 | } |
| 325 | 375 | |
| 326 | 376 | } else { |
| 327 | 377 | /* Cannot allocate memory for search pattern */ |
| @@ -350,70 +400,59 @@ | ||
| 350 | 400 | */ |
| 351 | 401 | static struct _wdirent* |
| 352 | 402 | _wreaddir( |
| 353 | 403 | _WDIR *dirp) |
| 354 | 404 | { |
| 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; | |
| 388 | 434 | if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { |
| 389 | - dirp->ent.d_type = DT_CHR; | |
| 435 | + entp->d_type = DT_CHR; | |
| 390 | 436 | } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { |
| 391 | - dirp->ent.d_type = DT_DIR; | |
| 437 | + entp->d_type = DT_DIR; | |
| 392 | 438 | } else { |
| 393 | - dirp->ent.d_type = DT_REG; | |
| 439 | + entp->d_type = DT_REG; | |
| 394 | 440 | } |
| 395 | 441 | |
| 396 | 442 | /* 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); | |
| 399 | 445 | |
| 400 | 446 | } else { |
| 401 | 447 | |
| 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 | + | |
| 412 | 451 | } |
| 413 | 452 | |
| 414 | - return &dirp->ent; | |
| 453 | + return entp; | |
| 415 | 454 | } |
| 416 | 455 | |
| 417 | 456 | /* |
| 418 | 457 | * Close directory stream opened by opendir() function. This invalidates the |
| 419 | 458 | * DIR structure as well as any directory entry read previously by |
| @@ -442,11 +481,11 @@ | ||
| 442 | 481 | free (dirp); |
| 443 | 482 | ok = /*success*/0; |
| 444 | 483 | |
| 445 | 484 | } else { |
| 446 | 485 | /* Invalid directory stream */ |
| 447 | - DIRENT_SET_ERRNO (EBADF); | |
| 486 | + dirent_set_errno (EBADF); | |
| 448 | 487 | ok = /*failure*/-1; |
| 449 | 488 | } |
| 450 | 489 | return ok; |
| 451 | 490 | } |
| 452 | 491 | |
| @@ -456,26 +495,81 @@ | ||
| 456 | 495 | */ |
| 457 | 496 | static void |
| 458 | 497 | _wrewinddir( |
| 459 | 498 | _WDIR* dirp) |
| 460 | 499 | { |
| 461 | - if (dirp != NULL) { | |
| 462 | - /* release search handle */ | |
| 500 | + if (dirp) { | |
| 501 | + /* Release existing search handle */ | |
| 463 | 502 | if (dirp->handle != INVALID_HANDLE_VALUE) { |
| 464 | 503 | FindClose (dirp->handle); |
| 465 | 504 | } |
| 466 | 505 | |
| 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; | |
| 472 | 556 | } 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; | |
| 475 | 561 | } |
| 562 | + | |
| 563 | + } else { | |
| 564 | + | |
| 565 | + /* End of directory stream reached */ | |
| 566 | + p = NULL; | |
| 567 | + | |
| 476 | 568 | } |
| 569 | + | |
| 570 | + return p; | |
| 477 | 571 | } |
| 478 | 572 | |
| 479 | 573 | /* |
| 480 | 574 | * Open directory stream using plain old C-string. |
| 481 | 575 | */ |
| @@ -482,58 +576,34 @@ | ||
| 482 | 576 | static DIR* |
| 483 | 577 | opendir( |
| 484 | 578 | const char *dirname) |
| 485 | 579 | { |
| 486 | 580 | struct DIR *dirp; |
| 487 | - errno_t error = 0; | |
| 581 | + int error; | |
| 488 | 582 | |
| 489 | 583 | /* 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); | |
| 492 | 586 | return NULL; |
| 493 | 587 | } |
| 494 | 588 | |
| 495 | - /* Allocate memory for multi-byte string directory structures */ | |
| 589 | + /* Allocate memory for DIR structure */ | |
| 496 | 590 | dirp = (DIR*) malloc (sizeof (struct DIR)); |
| 497 | 591 | if (dirp) { |
| 498 | 592 | wchar_t wname[PATH_MAX + 1]; |
| 499 | 593 | size_t n; |
| 500 | 594 | |
| 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 */ | |
| 524 | 601 | dirp->wdirp = _wopendir (wname); |
| 525 | 602 | 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; | |
| 535 | 605 | } else { |
| 536 | 606 | /* Failed to open directory stream */ |
| 537 | 607 | error = 1; |
| 538 | 608 | } |
| 539 | 609 | |
| @@ -562,95 +632,99 @@ | ||
| 562 | 632 | } |
| 563 | 633 | |
| 564 | 634 | /* |
| 565 | 635 | * Read next directory entry. |
| 566 | 636 | * |
| 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. | |
| 575 | 646 | */ |
| 576 | 647 | static struct dirent* |
| 577 | 648 | readdir( |
| 578 | 649 | DIR *dirp) |
| 579 | 650 | { |
| 580 | - struct dirent *p; | |
| 581 | - struct _wdirent *wp; | |
| 651 | + WIN32_FIND_DATAW *datap; | |
| 652 | + struct dirent *entp; | |
| 582 | 653 | |
| 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) { | |
| 586 | 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); | |
| 587 | 663 | |
| 588 | 664 | /* |
| 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. | |
| 597 | 673 | */ |
| 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; | |
| 611 | 683 | |
| 612 | 684 | /* Initialize directory entry for return */ |
| 613 | - p = &dirp->ent; | |
| 685 | + entp = &dirp->ent; | |
| 614 | 686 | |
| 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; | |
| 617 | 689 | |
| 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 | + } | |
| 620 | 699 | |
| 621 | 700 | /* 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); | |
| 625 | 703 | |
| 626 | 704 | } else { |
| 627 | - | |
| 628 | 705 | /* |
| 629 | 706 | * Cannot convert file name to multi-byte string so construct |
| 630 | 707 | * an errornous directory entry and return that. Note that |
| 631 | 708 | * we cannot return NULL as that would stop the processing |
| 632 | 709 | * of directory entries completely. |
| 633 | 710 | */ |
| 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; | |
| 642 | 718 | } |
| 643 | 719 | |
| 644 | 720 | } else { |
| 645 | - | |
| 646 | - /* End of directory stream */ | |
| 647 | - p = NULL; | |
| 648 | - | |
| 721 | + /* No more directory entries */ | |
| 722 | + entp = NULL; | |
| 649 | 723 | } |
| 650 | 724 | |
| 651 | - return p; | |
| 725 | + return entp; | |
| 652 | 726 | } |
| 653 | 727 | |
| 654 | 728 | /* |
| 655 | 729 | * Close directory stream. |
| 656 | 730 | */ |
| @@ -667,13 +741,15 @@ | ||
| 667 | 741 | |
| 668 | 742 | /* Release multi-byte character version */ |
| 669 | 743 | free (dirp); |
| 670 | 744 | |
| 671 | 745 | } else { |
| 746 | + | |
| 672 | 747 | /* Invalid directory stream */ |
| 673 | - DIRENT_SET_ERRNO (EBADF); | |
| 748 | + dirent_set_errno (EBADF); | |
| 674 | 749 | ok = /*failure*/-1; |
| 750 | + | |
| 675 | 751 | } |
| 676 | 752 | return ok; |
| 677 | 753 | } |
| 678 | 754 | |
| 679 | 755 | /* |
| @@ -684,12 +760,130 @@ | ||
| 684 | 760 | DIR* dirp) |
| 685 | 761 | { |
| 686 | 762 | /* Rewind wide-character string directory stream */ |
| 687 | 763 | _wrewinddir (dirp->wdirp); |
| 688 | 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 | +} | |
| 689 | 883 | |
| 690 | 884 | |
| 691 | 885 | #ifdef __cplusplus |
| 692 | 886 | } |
| 693 | 887 | #endif |
| 694 | 888 | #endif /*DIRENT_H*/ |
| 695 | 889 | |
| 696 | 890 |
| --- 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 |
+3
-4
| --- www/checkin_names.wiki | ||
| +++ www/checkin_names.wiki | ||
| @@ -14,12 +14,11 @@ | ||
| 14 | 14 | <li> Special names: |
| 15 | 15 | <ul> |
| 16 | 16 | <li> <b>tip</b> |
| 17 | 17 | <li> <b>current</b> |
| 18 | 18 | <li> <b>next</b> |
| 19 | -<li> <b>previous</b> | |
| 20 | -<li> <b>ckout</b> | |
| 19 | +<li> <b>previous</b> or <b>prev</b> | |
| 21 | 20 | </ul> |
| 22 | 21 | </ul> |
| 23 | 22 | </td></tr> |
| 24 | 23 | </table> |
| 25 | 24 | Many Fossil [/help|commands] and [./webui.wiki | web-interface] URLs accept |
| @@ -194,12 +193,12 @@ | ||
| 194 | 193 | equivalent to the timestamp tag "5000-01-01". |
| 195 | 194 | |
| 196 | 195 | If the command is being run from a working check-out (not against a bare |
| 197 | 196 | repository) then a few extra tags apply. The "current" tag means the |
| 198 | 197 | 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. | |
| 201 | 200 | |
| 202 | 201 | <h2>Additional Examples</h2> |
| 203 | 202 | |
| 204 | 203 | To view the changes in the most recent check-in prior to the version currently |
| 205 | 204 | checked out: |
| 206 | 205 | |
| 207 | 206 | 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 |
+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 "commit" | |
| 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 "commit" | |
| 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 "commit" |
| 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 "commit" |
| 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 |
+2
| --- www/index.wiki | ||
| +++ www/index.wiki | ||
| @@ -149,10 +149,12 @@ | ||
| 149 | 149 | * How to [./server.wiki | set up a server] for your repository. |
| 150 | 150 | * Customizing the [./custom_ticket.wiki | ticket system]. |
| 151 | 151 | * Methods to [./checkin_names.wiki | identify a specific check-in]. |
| 152 | 152 | * [./inout.wiki | Import and export] from and to Git. |
| 153 | 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). | |
| 154 | 156 | |
| 155 | 157 | <h3>Links For Fossil Developer:</h3> |
| 156 | 158 | |
| 157 | 159 | * [./contribute.wiki | Contributing] code or documentation to the |
| 158 | 160 | Fossil project. |
| 159 | 161 |
| --- 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 |
+1
| --- www/mkindex.tcl | ||
| +++ www/mkindex.tcl | ||
| @@ -21,10 +21,11 @@ | ||
| 21 | 21 | delta_format.wiki {Fossil Delta Format} |
| 22 | 22 | embeddeddoc.wiki {Embedded Project Documentation} |
| 23 | 23 | event.wiki {Events} |
| 24 | 24 | faq.wiki {Frequently Asked Questions} |
| 25 | 25 | fileformat.wiki {Fossil File Format} |
| 26 | + fiveminutes.wiki {Update and Running in 5 Minutes as a Single User} | |
| 26 | 27 | foss-cklist.wiki {Checklist For Successful Open-Source Projects} |
| 27 | 28 | fossil-v-git.wiki {Fossil Versus Git} |
| 28 | 29 | index.wiki {Home Page} |
| 29 | 30 | inout.wiki {Import And Export To And From Git} |
| 30 | 31 | makefile.wiki {The Fossil Build Process} |
| 31 | 32 |
| --- 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 @@ | ||
| 10 | 10 | <li> [/help | Command-line help] |
| 11 | 11 | </ul> |
| 12 | 12 | <a name="pindex"></a> |
| 13 | 13 | <h2>Permuted Index:</h2> |
| 14 | 14 | <ul> |
| 15 | +<li><a href="fiveminutes.wiki">5 Minutes as a Single User — Update and Running in</a></li> | |
| 15 | 16 | <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li> |
| 16 | 17 | <li><a href="copyright-release.html">Agreement — Contributor License</a></li> |
| 17 | 18 | <li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> |
| 19 | +<li><a href="fiveminutes.wiki">as a Single User — Update and Running in 5 Minutes</a></li> | |
| 18 | 20 | <li><a href="faq.wiki">Asked Questions — Frequently</a></li> |
| 19 | 21 | <li><a href="password.wiki">Authentication — Password Management And</a></li> |
| 20 | 22 | <li><a href="private.wiki">Branches — Creating, Syncing, and Deleting Private</a></li> |
| 21 | 23 | <li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li> |
| 22 | 24 | <li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li> |
| @@ -90,10 +92,11 @@ | ||
| 90 | 92 | <li><a href="selfcheck.wiki">Integrity Self Checks — Fossil Repository</a></li> |
| 91 | 93 | <li><a href="webui.wiki">Interface — The Fossil Web</a></li> |
| 92 | 94 | <li><a href="copyright-release.html">License Agreement — Contributor</a></li> |
| 93 | 95 | <li><a href="password.wiki">Management And Authentication — Password</a></li> |
| 94 | 96 | <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> |
| 97 | +<li><a href="fiveminutes.wiki">Minutes as a Single User — Update and Running in 5</a></li> | |
| 95 | 98 | <li><a href="checkin_names.wiki">Names — Checkin And Version</a></li> |
| 96 | 99 | <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> |
| 97 | 100 | <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> |
| 98 | 101 | <li><a href="pop.wiki">Operations — Principles Of</a></li> |
| 99 | 102 | <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> |
| @@ -115,16 +118,18 @@ | ||
| 115 | 118 | <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li> |
| 116 | 119 | <li><a href="selfhost.wiki">Repositories — Fossil Self Hosting</a></li> |
| 117 | 120 | <li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> |
| 118 | 121 | <li><a href="selfcheck.wiki">Repository Integrity Self Checks — Fossil</a></li> |
| 119 | 122 | <li><a href="reviews.wiki">Reviews</a></li> |
| 123 | +<li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User — Update and</a></li> | |
| 120 | 124 | <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are</a></li> |
| 121 | 125 | <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> |
| 122 | 126 | <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> |
| 123 | 127 | <li><a href="server.wiki">Server — How To Configure A Fossil</a></li> |
| 124 | 128 | <li><a href="settings.wiki">Settings — Fossil</a></li> |
| 125 | 129 | <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li> |
| 130 | +<li><a href="fiveminutes.wiki">Single User — Update and Running in 5 Minutes as a</a></li> | |
| 126 | 131 | <li><a href="style.wiki">Source Code Style Guidelines</a></li> |
| 127 | 132 | <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li> |
| 128 | 133 | <li><a href="ssl.wiki">SSL with Fossil — Using</a></li> |
| 129 | 134 | <li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> |
| 130 | 135 | <li><a href="stats.wiki">Statistics — Performance</a></li> |
| @@ -140,13 +145,15 @@ | ||
| 140 | 145 | <li><a href="sync.wiki">The Fossil Sync Protocol</a></li> |
| 141 | 146 | <li><a href="webui.wiki">The Fossil Web Interface</a></li> |
| 142 | 147 | <li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li> |
| 143 | 148 | <li><a href="custom_ticket.wiki">Ticket System — Customizing The</a></li> |
| 144 | 149 | <li><a href="bugtheory.wiki">Tracking In Fossil — 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 — Update and Running in 5 Minutes as a Single</a></li> | |
| 145 | 152 | <li><a href="ssl.wiki">Using SSL with Fossil</a></li> |
| 146 | 153 | <li><a href="checkin_names.wiki">Version Names — Checkin And</a></li> |
| 147 | 154 | <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> |
| 148 | 155 | <li><a href="webui.wiki">Web Interface — The Fossil</a></li> |
| 149 | 156 | <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — Quotes:</a></li> |
| 150 | 157 | <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> |
| 151 | 158 | <li><a href="ssl.wiki">with Fossil — Using SSL</a></li> |
| 152 | 159 | </ul> |
| 153 | 160 |
| --- 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 — Contributor License</a></li> |
| 17 | <li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> |
| 18 | <li><a href="faq.wiki">Asked Questions — Frequently</a></li> |
| 19 | <li><a href="password.wiki">Authentication — Password Management And</a></li> |
| 20 | <li><a href="private.wiki">Branches — 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 — Fossil Repository</a></li> |
| 91 | <li><a href="webui.wiki">Interface — The Fossil Web</a></li> |
| 92 | <li><a href="copyright-release.html">License Agreement — Contributor</a></li> |
| 93 | <li><a href="password.wiki">Management And Authentication — Password</a></li> |
| 94 | <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> |
| 95 | <li><a href="checkin_names.wiki">Names — Checkin And Version</a></li> |
| 96 | <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> |
| 97 | <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> |
| 98 | <li><a href="pop.wiki">Operations — Principles Of</a></li> |
| 99 | <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — 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 — Fossil Self Hosting</a></li> |
| 117 | <li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> |
| 118 | <li><a href="selfcheck.wiki">Repository Integrity Self Checks — 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 — Quotes: What People Are</a></li> |
| 121 | <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> |
| 122 | <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> |
| 123 | <li><a href="server.wiki">Server — How To Configure A Fossil</a></li> |
| 124 | <li><a href="settings.wiki">Settings — 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 — Using</a></li> |
| 129 | <li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> |
| 130 | <li><a href="stats.wiki">Statistics — 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 — Customizing The</a></li> |
| 144 | <li><a href="bugtheory.wiki">Tracking In Fossil — Bug</a></li> |
| 145 | <li><a href="ssl.wiki">Using SSL with Fossil</a></li> |
| 146 | <li><a href="checkin_names.wiki">Version Names — Checkin And</a></li> |
| 147 | <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> |
| 148 | <li><a href="webui.wiki">Web Interface — The Fossil</a></li> |
| 149 | <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — Quotes:</a></li> |
| 150 | <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> |
| 151 | <li><a href="ssl.wiki">with Fossil — 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 — 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 — Contributor License</a></li> |
| 18 | <li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> |
| 19 | <li><a href="fiveminutes.wiki">as a Single User — Update and Running in 5 Minutes</a></li> |
| 20 | <li><a href="faq.wiki">Asked Questions — Frequently</a></li> |
| 21 | <li><a href="password.wiki">Authentication — Password Management And</a></li> |
| 22 | <li><a href="private.wiki">Branches — 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 — Fossil Repository</a></li> |
| 93 | <li><a href="webui.wiki">Interface — The Fossil Web</a></li> |
| 94 | <li><a href="copyright-release.html">License Agreement — Contributor</a></li> |
| 95 | <li><a href="password.wiki">Management And Authentication — Password</a></li> |
| 96 | <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> |
| 97 | <li><a href="fiveminutes.wiki">Minutes as a Single User — Update and Running in 5</a></li> |
| 98 | <li><a href="checkin_names.wiki">Names — Checkin And Version</a></li> |
| 99 | <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> |
| 100 | <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> |
| 101 | <li><a href="pop.wiki">Operations — Principles Of</a></li> |
| 102 | <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — 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 — Fossil Self Hosting</a></li> |
| 120 | <li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> |
| 121 | <li><a href="selfcheck.wiki">Repository Integrity Self Checks — 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 — Update and</a></li> |
| 124 | <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are</a></li> |
| 125 | <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> |
| 126 | <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> |
| 127 | <li><a href="server.wiki">Server — How To Configure A Fossil</a></li> |
| 128 | <li><a href="settings.wiki">Settings — Fossil</a></li> |
| 129 | <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li> |
| 130 | <li><a href="fiveminutes.wiki">Single User — 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 — Using</a></li> |
| 134 | <li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> |
| 135 | <li><a href="stats.wiki">Statistics — 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 — Customizing The</a></li> |
| 149 | <li><a href="bugtheory.wiki">Tracking In Fossil — 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 — 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 — Checkin And</a></li> |
| 154 | <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> |
| 155 | <li><a href="webui.wiki">Web Interface — The Fossil</a></li> |
| 156 | <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — Quotes:</a></li> |
| 157 | <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> |
| 158 | <li><a href="ssl.wiki">with Fossil — Using SSL</a></li> |
| 159 | </ul> |
| 160 |