Fossil SCM

Merge from trunk.

ashish 2014-01-16 02:15 ashish-ipv6 merge
Commit eb5a49f83500c79a5656f551412b2fcc70045ad6
+1 -1
--- auto.def
+++ auto.def
@@ -29,11 +29,11 @@
2929
# Find tclsh for the test suite. Can't yet use jimsh for this.
3030
cc-check-progs tclsh
3131
3232
define EXTRA_CFLAGS ""
3333
define EXTRA_LDFLAGS ""
34
-define USE_SYSTEM_SQLITE ""
34
+define USE_SYSTEM_SQLITE 0
3535
3636
if {![opt-bool internal-sqlite]} {
3737
proc find_internal_sqlite {} {
3838
3939
# On some systems (slackware), libsqlite3 requires -ldl to link. So
4040
--- auto.def
+++ auto.def
@@ -29,11 +29,11 @@
29 # Find tclsh for the test suite. Can't yet use jimsh for this.
30 cc-check-progs tclsh
31
32 define EXTRA_CFLAGS ""
33 define EXTRA_LDFLAGS ""
34 define USE_SYSTEM_SQLITE ""
35
36 if {![opt-bool internal-sqlite]} {
37 proc find_internal_sqlite {} {
38
39 # On some systems (slackware), libsqlite3 requires -ldl to link. So
40
--- auto.def
+++ auto.def
@@ -29,11 +29,11 @@
29 # Find tclsh for the test suite. Can't yet use jimsh for this.
30 cc-check-progs tclsh
31
32 define EXTRA_CFLAGS ""
33 define EXTRA_LDFLAGS ""
34 define USE_SYSTEM_SQLITE 0
35
36 if {![opt-bool internal-sqlite]} {
37 proc find_internal_sqlite {} {
38
39 # On some systems (slackware), libsqlite3 requires -ldl to link. So
40
+1 -1
--- auto.def
+++ auto.def
@@ -29,11 +29,11 @@
2929
# Find tclsh for the test suite. Can't yet use jimsh for this.
3030
cc-check-progs tclsh
3131
3232
define EXTRA_CFLAGS ""
3333
define EXTRA_LDFLAGS ""
34
-define USE_SYSTEM_SQLITE ""
34
+define USE_SYSTEM_SQLITE 0
3535
3636
if {![opt-bool internal-sqlite]} {
3737
proc find_internal_sqlite {} {
3838
3939
# On some systems (slackware), libsqlite3 requires -ldl to link. So
4040
--- auto.def
+++ auto.def
@@ -29,11 +29,11 @@
29 # Find tclsh for the test suite. Can't yet use jimsh for this.
30 cc-check-progs tclsh
31
32 define EXTRA_CFLAGS ""
33 define EXTRA_LDFLAGS ""
34 define USE_SYSTEM_SQLITE ""
35
36 if {![opt-bool internal-sqlite]} {
37 proc find_internal_sqlite {} {
38
39 # On some systems (slackware), libsqlite3 requires -ldl to link. So
40
--- auto.def
+++ auto.def
@@ -29,11 +29,11 @@
29 # Find tclsh for the test suite. Can't yet use jimsh for this.
30 cc-check-progs tclsh
31
32 define EXTRA_CFLAGS ""
33 define EXTRA_LDFLAGS ""
34 define USE_SYSTEM_SQLITE 0
35
36 if {![opt-bool internal-sqlite]} {
37 proc find_internal_sqlite {} {
38
39 # On some systems (slackware), libsqlite3 requires -ldl to link. So
40
+5
--- src/add.c
+++ src/add.c
@@ -270,10 +270,15 @@
270270
/* Load the names of all files that are to be added into sfile temp table */
271271
for(i=2; i<g.argc; i++){
272272
char *zName;
273273
int isDir;
274274
Blob fullName;
275
+
276
+ /* file_tree_name() throws a fatal error if g.argv[i] is outside of the
277
+ ** checkout. */
278
+ file_tree_name(g.argv[i], &fullName, 1);
279
+ blob_reset(&fullName);
275280
276281
file_canonical_name(g.argv[i], &fullName, 0);
277282
zName = blob_str(&fullName);
278283
isDir = file_wd_isdir(zName);
279284
if( isDir==1 ){
280285
--- src/add.c
+++ src/add.c
@@ -270,10 +270,15 @@
270 /* Load the names of all files that are to be added into sfile temp table */
271 for(i=2; i<g.argc; i++){
272 char *zName;
273 int isDir;
274 Blob fullName;
 
 
 
 
 
275
276 file_canonical_name(g.argv[i], &fullName, 0);
277 zName = blob_str(&fullName);
278 isDir = file_wd_isdir(zName);
279 if( isDir==1 ){
280
--- src/add.c
+++ src/add.c
@@ -270,10 +270,15 @@
270 /* Load the names of all files that are to be added into sfile temp table */
271 for(i=2; i<g.argc; i++){
272 char *zName;
273 int isDir;
274 Blob fullName;
275
276 /* file_tree_name() throws a fatal error if g.argv[i] is outside of the
277 ** checkout. */
278 file_tree_name(g.argv[i], &fullName, 1);
279 blob_reset(&fullName);
280
281 file_canonical_name(g.argv[i], &fullName, 0);
282 zName = blob_str(&fullName);
283 isDir = file_wd_isdir(zName);
284 if( isDir==1 ){
285
+5 -5
--- src/attach.c
+++ src/attach.c
@@ -40,16 +40,16 @@
4040
Stmt q;
4141
4242
if( zPage && zTkt ) zTkt = 0;
4343
login_check_credentials();
4444
blob_zero(&sql);
45
- blob_append(&sql,
46
- "SELECT datetime(mtime,'localtime'), src, target, filename,"
45
+ blob_appendf(&sql,
46
+ "SELECT datetime(mtime%s), src, target, filename,"
4747
" comment, user,"
4848
" (SELECT uuid FROM blob WHERE rid=attachid), attachid"
4949
" FROM attachment",
50
- -1
50
+ timeline_utc()
5151
);
5252
if( zPage ){
5353
if( g.perm.RdWiki==0 ) login_needed();
5454
style_header("Attachments To %h", zPage);
5555
blob_appendf(&sql, " WHERE target=%Q", zPage);
@@ -553,16 +553,16 @@
553553
const char *zHeader /* Header to display with attachments */
554554
){
555555
int cnt = 0;
556556
Stmt q;
557557
db_prepare(&q,
558
- "SELECT datetime(mtime,'localtime'), filename, user,"
558
+ "SELECT datetime(mtime%s), filename, user,"
559559
" (SELECT uuid FROM blob WHERE rid=attachid), src"
560560
" FROM attachment"
561561
" WHERE isLatest AND src!='' AND target=%Q"
562562
" ORDER BY mtime DESC",
563
- zTarget
563
+ timeline_utc(), zTarget
564564
);
565565
while( db_step(&q)==SQLITE_ROW ){
566566
const char *zDate = db_column_text(&q, 0);
567567
const char *zFile = db_column_text(&q, 1);
568568
const char *zUser = db_column_text(&q, 2);
569569
--- src/attach.c
+++ src/attach.c
@@ -40,16 +40,16 @@
40 Stmt q;
41
42 if( zPage && zTkt ) zTkt = 0;
43 login_check_credentials();
44 blob_zero(&sql);
45 blob_append(&sql,
46 "SELECT datetime(mtime,'localtime'), src, target, filename,"
47 " comment, user,"
48 " (SELECT uuid FROM blob WHERE rid=attachid), attachid"
49 " FROM attachment",
50 -1
51 );
52 if( zPage ){
53 if( g.perm.RdWiki==0 ) login_needed();
54 style_header("Attachments To %h", zPage);
55 blob_appendf(&sql, " WHERE target=%Q", zPage);
@@ -553,16 +553,16 @@
553 const char *zHeader /* Header to display with attachments */
554 ){
555 int cnt = 0;
556 Stmt q;
557 db_prepare(&q,
558 "SELECT datetime(mtime,'localtime'), filename, user,"
559 " (SELECT uuid FROM blob WHERE rid=attachid), src"
560 " FROM attachment"
561 " WHERE isLatest AND src!='' AND target=%Q"
562 " ORDER BY mtime DESC",
563 zTarget
564 );
565 while( db_step(&q)==SQLITE_ROW ){
566 const char *zDate = db_column_text(&q, 0);
567 const char *zFile = db_column_text(&q, 1);
568 const char *zUser = db_column_text(&q, 2);
569
--- src/attach.c
+++ src/attach.c
@@ -40,16 +40,16 @@
40 Stmt q;
41
42 if( zPage && zTkt ) zTkt = 0;
43 login_check_credentials();
44 blob_zero(&sql);
45 blob_appendf(&sql,
46 "SELECT datetime(mtime%s), src, target, filename,"
47 " comment, user,"
48 " (SELECT uuid FROM blob WHERE rid=attachid), attachid"
49 " FROM attachment",
50 timeline_utc()
51 );
52 if( zPage ){
53 if( g.perm.RdWiki==0 ) login_needed();
54 style_header("Attachments To %h", zPage);
55 blob_appendf(&sql, " WHERE target=%Q", zPage);
@@ -553,16 +553,16 @@
553 const char *zHeader /* Header to display with attachments */
554 ){
555 int cnt = 0;
556 Stmt q;
557 db_prepare(&q,
558 "SELECT datetime(mtime%s), filename, user,"
559 " (SELECT uuid FROM blob WHERE rid=attachid), src"
560 " FROM attachment"
561 " WHERE isLatest AND src!='' AND target=%Q"
562 " ORDER BY mtime DESC",
563 timeline_utc(), zTarget
564 );
565 while( db_step(&q)==SQLITE_ROW ){
566 const char *zDate = db_column_text(&q, 0);
567 const char *zFile = db_column_text(&q, 1);
568 const char *zUser = db_column_text(&q, 2);
569
+10 -7
--- src/blob.c
+++ src/blob.c
@@ -223,10 +223,19 @@
223223
** Any prior data in the blob is discarded.
224224
*/
225225
void blob_set(Blob *pBlob, const char *zStr){
226226
blob_init(pBlob, zStr, -1);
227227
}
228
+
229
+/*
230
+** Initialize a blob to a nul-terminated string obtained from fossil_malloc().
231
+** The blob will take responsibility for freeing the string.
232
+*/
233
+void blob_set_dynamic(Blob *pBlob, char *zStr){
234
+ blob_init(pBlob, zStr, -1);
235
+ pBlob->xRealloc = blobReallocMalloc;
236
+}
228237
229238
/*
230239
** Initialize a blob to an empty string.
231240
*/
232241
void blob_zero(Blob *pBlob){
@@ -1095,21 +1104,18 @@
10951104
** to be UTF-8 already, so no conversion is done.
10961105
*/
10971106
void blob_to_utf8_no_bom(Blob *pBlob, int useMbcs){
10981107
char *zUtf8;
10991108
int bomSize = 0;
1100
-#if defined(_WIN32) || defined(__CYGWIN__)
11011109
int bomReverse = 0;
1102
-#endif
11031110
if( starts_with_utf8_bom(pBlob, &bomSize) ){
11041111
struct Blob temp;
11051112
zUtf8 = blob_str(pBlob) + bomSize;
11061113
blob_zero(&temp);
11071114
blob_append(&temp, zUtf8, -1);
11081115
blob_swap(pBlob, &temp);
11091116
blob_reset(&temp);
1110
-#if defined(_WIN32) || defined(__CYGWIN__)
11111117
}else if( starts_with_utf16_bom(pBlob, &bomSize, &bomReverse) ){
11121118
zUtf8 = blob_buffer(pBlob);
11131119
if( bomReverse ){
11141120
/* Found BOM, but with reversed bytes */
11151121
unsigned int i = blob_size(pBlob);
@@ -1122,18 +1128,15 @@
11221128
}
11231129
/* Make sure the blob contains two terminating 0-bytes */
11241130
blob_append(pBlob, "", 1);
11251131
zUtf8 = blob_str(pBlob) + bomSize;
11261132
zUtf8 = fossil_unicode_to_utf8(zUtf8);
1127
- blob_zero(pBlob);
1128
- blob_append(pBlob, zUtf8, -1);
1129
- fossil_unicode_free(zUtf8);
1130
-#endif /* _WIN32 || __CYGWIN__ */
1133
+ blob_set_dynamic(pBlob, zUtf8);
11311134
#if defined(_WIN32)
11321135
}else if( useMbcs ){
11331136
zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
11341137
blob_reset(pBlob);
11351138
blob_append(pBlob, zUtf8, -1);
11361139
fossil_mbcs_free(zUtf8);
11371140
#endif /* _WIN32 */
11381141
}
11391142
}
11401143
--- src/blob.c
+++ src/blob.c
@@ -223,10 +223,19 @@
223 ** Any prior data in the blob is discarded.
224 */
225 void blob_set(Blob *pBlob, const char *zStr){
226 blob_init(pBlob, zStr, -1);
227 }
 
 
 
 
 
 
 
 
 
228
229 /*
230 ** Initialize a blob to an empty string.
231 */
232 void blob_zero(Blob *pBlob){
@@ -1095,21 +1104,18 @@
1095 ** to be UTF-8 already, so no conversion is done.
1096 */
1097 void blob_to_utf8_no_bom(Blob *pBlob, int useMbcs){
1098 char *zUtf8;
1099 int bomSize = 0;
1100 #if defined(_WIN32) || defined(__CYGWIN__)
1101 int bomReverse = 0;
1102 #endif
1103 if( starts_with_utf8_bom(pBlob, &bomSize) ){
1104 struct Blob temp;
1105 zUtf8 = blob_str(pBlob) + bomSize;
1106 blob_zero(&temp);
1107 blob_append(&temp, zUtf8, -1);
1108 blob_swap(pBlob, &temp);
1109 blob_reset(&temp);
1110 #if defined(_WIN32) || defined(__CYGWIN__)
1111 }else if( starts_with_utf16_bom(pBlob, &bomSize, &bomReverse) ){
1112 zUtf8 = blob_buffer(pBlob);
1113 if( bomReverse ){
1114 /* Found BOM, but with reversed bytes */
1115 unsigned int i = blob_size(pBlob);
@@ -1122,18 +1128,15 @@
1122 }
1123 /* Make sure the blob contains two terminating 0-bytes */
1124 blob_append(pBlob, "", 1);
1125 zUtf8 = blob_str(pBlob) + bomSize;
1126 zUtf8 = fossil_unicode_to_utf8(zUtf8);
1127 blob_zero(pBlob);
1128 blob_append(pBlob, zUtf8, -1);
1129 fossil_unicode_free(zUtf8);
1130 #endif /* _WIN32 || __CYGWIN__ */
1131 #if defined(_WIN32)
1132 }else if( useMbcs ){
1133 zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
1134 blob_reset(pBlob);
1135 blob_append(pBlob, zUtf8, -1);
1136 fossil_mbcs_free(zUtf8);
1137 #endif /* _WIN32 */
1138 }
1139 }
1140
--- src/blob.c
+++ src/blob.c
@@ -223,10 +223,19 @@
223 ** Any prior data in the blob is discarded.
224 */
225 void blob_set(Blob *pBlob, const char *zStr){
226 blob_init(pBlob, zStr, -1);
227 }
228
229 /*
230 ** Initialize a blob to a nul-terminated string obtained from fossil_malloc().
231 ** The blob will take responsibility for freeing the string.
232 */
233 void blob_set_dynamic(Blob *pBlob, char *zStr){
234 blob_init(pBlob, zStr, -1);
235 pBlob->xRealloc = blobReallocMalloc;
236 }
237
238 /*
239 ** Initialize a blob to an empty string.
240 */
241 void blob_zero(Blob *pBlob){
@@ -1095,21 +1104,18 @@
1104 ** to be UTF-8 already, so no conversion is done.
1105 */
1106 void blob_to_utf8_no_bom(Blob *pBlob, int useMbcs){
1107 char *zUtf8;
1108 int bomSize = 0;
 
1109 int bomReverse = 0;
 
1110 if( starts_with_utf8_bom(pBlob, &bomSize) ){
1111 struct Blob temp;
1112 zUtf8 = blob_str(pBlob) + bomSize;
1113 blob_zero(&temp);
1114 blob_append(&temp, zUtf8, -1);
1115 blob_swap(pBlob, &temp);
1116 blob_reset(&temp);
 
1117 }else if( starts_with_utf16_bom(pBlob, &bomSize, &bomReverse) ){
1118 zUtf8 = blob_buffer(pBlob);
1119 if( bomReverse ){
1120 /* Found BOM, but with reversed bytes */
1121 unsigned int i = blob_size(pBlob);
@@ -1122,18 +1128,15 @@
1128 }
1129 /* Make sure the blob contains two terminating 0-bytes */
1130 blob_append(pBlob, "", 1);
1131 zUtf8 = blob_str(pBlob) + bomSize;
1132 zUtf8 = fossil_unicode_to_utf8(zUtf8);
1133 blob_set_dynamic(pBlob, zUtf8);
 
 
 
1134 #if defined(_WIN32)
1135 }else if( useMbcs ){
1136 zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
1137 blob_reset(pBlob);
1138 blob_append(pBlob, zUtf8, -1);
1139 fossil_mbcs_free(zUtf8);
1140 #endif /* _WIN32 */
1141 }
1142 }
1143
+443 -26
--- src/browse.c
+++ src/browse.c
@@ -71,23 +71,29 @@
7171
** There is no hyperlink on the file element of the path.
7272
**
7373
** The computed string is appended to the pOut blob. pOut should
7474
** have already been initialized.
7575
*/
76
-void hyperlinked_path(const char *zPath, Blob *pOut, const char *zCI){
76
+void hyperlinked_path(
77
+ const char *zPath, /* Path to render */
78
+ Blob *pOut, /* Write into this blob */
79
+ const char *zCI, /* check-in name, or NULL */
80
+ const char *zURI, /* "dir" or "tree" */
81
+ const char *zREx /* Extra query parameters */
82
+){
7783
int i, j;
7884
char *zSep = "";
7985
8086
for(i=0; zPath[i]; i=j){
8187
for(j=i; zPath[j] && zPath[j]!='/'; j++){}
8288
if( zPath[j] && g.perm.Hyperlink ){
8389
if( zCI ){
84
- char *zLink = href("%R/dir?ci=%S&name=%#T", zCI, j, zPath);
90
+ char *zLink = href("%R/%s?ci=%S&name=%#T%s", zURI, zCI, j, zPath,zREx);
8591
blob_appendf(pOut, "%s%z%#h</a>",
8692
zSep, zLink, j-i, &zPath[i]);
8793
}else{
88
- char *zLink = href("%R/dir?name=%#T", j, zPath);
94
+ char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
8995
blob_appendf(pOut, "%s%z%#h</a>",
9096
zSep, zLink, j-i, &zPath[i]);
9197
}
9298
}else{
9399
blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
@@ -101,11 +107,11 @@
101107
/*
102108
** WEBPAGE: dir
103109
**
104110
** Query parameters:
105111
**
106
-** name=PATH Directory to display. Required.
112
+** name=PATH Directory to display. Optional. Top-level if missing
107113
** ci=LABEL Show only files in this check-in. Optional.
108114
*/
109115
void page_dir(void){
110116
char *zD = fossil_strdup(P("name"));
111117
int nD = zD ? strlen(zD)+1 : 0;
@@ -118,18 +124,22 @@
118124
int rid = 0;
119125
char *zUuid = 0;
120126
Blob dirname;
121127
Manifest *pM = 0;
122128
const char *zSubdirLink;
123
- int linkTrunk = 1, linkTip = 1;
129
+ int linkTrunk = 1;
130
+ int linkTip = 1;
131
+ HQuery sURI;
124132
133
+ if( strcmp(PD("type",""),"tree")==0 ){ page_tree(); return; }
125134
login_check_credentials();
126135
if( !g.perm.Read ){ login_needed(); return; }
127136
while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
128137
style_header("File List");
129138
sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
130139
pathelementFunc, 0, 0);
140
+ url_initialize(&sURI, "dir");
131141
132142
/* If the name= parameter is an empty string, make it a NULL pointer */
133143
if( zD && strlen(zD)==0 ){ zD = 0; }
134144
135145
/* If a specific check-in is requested, fetch and parse it. If the
@@ -141,58 +151,57 @@
141151
if( pM ){
142152
int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
143153
linkTrunk = trunkRid && rid != trunkRid;
144154
linkTip = rid != symbolic_name_to_rid("tip", "ci");
145155
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
156
+ url_add_parameter(&sURI, "ci", zCI);
146157
}else{
147158
zCI = 0;
148159
}
149160
}
150161
151162
/* Compute the title of the page */
152163
blob_zero(&dirname);
153164
if( zD ){
165
+ url_add_parameter(&sURI, "name", zD);
154166
blob_append(&dirname, "in directory ", -1);
155
- hyperlinked_path(zD, &dirname, zCI);
167
+ hyperlinked_path(zD, &dirname, zCI, "dir", "");
156168
zPrefix = mprintf("%s/", zD);
157
- if( linkTrunk ){
158
- style_submenu_element("Trunk", "Trunk", "%R/dir?name=%t&ci=trunk",
159
- zD);
160
- }
161
- if ( linkTip ){
162
- style_submenu_element("Tip", "Tip", "%R/dir?name=%t&ci=tip", zD);
163
- }
169
+ style_submenu_element("Top-Level", "Top-Level", "%s",
170
+ url_render(&sURI, "name", 0, 0, 0));
164171
}else{
165172
blob_append(&dirname, "in the top-level directory", -1);
166173
zPrefix = "";
167
- if( linkTrunk ){
168
- style_submenu_element("Trunk", "Trunk", "%R/dir?ci=trunk");
169
- }
170
- if ( linkTip ){
171
- style_submenu_element("Tip", "Tip", "%R/dir?ci=tip");
172
- }
174
+ }
175
+ if( linkTrunk ){
176
+ style_submenu_element("Trunk", "Trunk", "%s",
177
+ url_render(&sURI, "ci", "trunk", 0, 0));
178
+ }
179
+ if( linkTip ){
180
+ style_submenu_element("Tip", "Tip", "%s",
181
+ url_render(&sURI, "ci", "tip", 0, 0));
173182
}
174183
if( zCI ){
175184
char zShort[20];
176185
memcpy(zShort, zUuid, 10);
177186
zShort[10] = 0;
178187
@ <h2>Files of check-in [%z(href("vinfo?name=%T",zUuid))%s(zShort)</a>]
179188
@ %s(blob_str(&dirname))</h2>
180189
zSubdirLink = mprintf("%R/dir?ci=%S&name=%T", zUuid, zPrefix);
181
- if( zD ){
182
- style_submenu_element("Top", "Top", "%R/dir?ci=%S", zUuid);
183
- style_submenu_element("All", "All", "%R/dir?name=%t", zD);
184
- }else{
185
- style_submenu_element("All", "All", "%R/dir");
190
+ if( nD==0 ){
186191
style_submenu_element("File Ages", "File Ages", "%R/fileage?name=%S",
187192
zUuid);
188193
}
189194
}else{
190195
@ <h2>The union of all files from all check-ins
191196
@ %s(blob_str(&dirname))</h2>
192197
zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
193198
}
199
+ style_submenu_element("All", "All", "%s",
200
+ url_render(&sURI, "ci", 0, 0, 0));
201
+ style_submenu_element("Tree-View", "Tree-View", "%s",
202
+ url_render(&sURI, "type", "tree", 0, 0));
194203
195204
/* Compute the temporary table "localfiles" containing the names
196205
** of all files and subdirectories in the zD[] directory.
197206
**
198207
** Subdirectory names begin with "/". This causes them to sort
@@ -287,10 +296,417 @@
287296
db_finalize(&q);
288297
manifest_destroy(pM);
289298
@ </ul></td></tr></table>
290299
style_footer();
291300
}
301
+
302
+/*
303
+** Objects used by the "tree" webpage.
304
+*/
305
+typedef struct FileTreeNode FileTreeNode;
306
+typedef struct FileTree FileTree;
307
+
308
+/*
309
+** A single line of the file hierarchy
310
+*/
311
+struct FileTreeNode {
312
+ FileTreeNode *pNext; /* Next line in sequence */
313
+ FileTreeNode *pPrev; /* Previous line */
314
+ FileTreeNode *pParent; /* Directory containing this line */
315
+ char *zName; /* Name of this entry. The "tail" */
316
+ char *zFullName; /* Full pathname of this entry */
317
+ char *zUuid; /* SHA1 hash of this file. May be NULL. */
318
+ unsigned nFullName; /* Length of zFullName */
319
+ unsigned iLevel; /* Levels of parent directories */
320
+ u8 isDir; /* True if there are children */
321
+ u8 isLast; /* True if this is the last child of its parent */
322
+};
323
+
324
+/*
325
+** A complete file hierarchy
326
+*/
327
+struct FileTree {
328
+ FileTreeNode *pFirst; /* First line of the list */
329
+ FileTreeNode *pLast; /* Last line of the list */
330
+};
331
+
332
+/*
333
+** Add one or more new FileTreeNodes to the FileTree object so that the
334
+** leaf object zPathname is at the end of the node list
335
+*/
336
+static void tree_add_node(
337
+ FileTree *pTree, /* Tree into which nodes are added */
338
+ const char *zPath, /* The full pathname of file to add */
339
+ const char *zUuid /* UUID of the file. Might be NULL. */
340
+){
341
+ int i;
342
+ FileTreeNode *pParent;
343
+ FileTreeNode *pChild;
344
+
345
+ pChild = pTree->pLast;
346
+ pParent = pChild ? pChild->pParent : 0;
347
+ while( pParent!=0 &&
348
+ ( strncmp(pParent->zFullName, zPath, pParent->nFullName)!=0
349
+ || zPath[pParent->nFullName]!='/' )
350
+ ){
351
+ pChild = pParent;
352
+ pParent = pChild->pParent;
353
+ }
354
+ i = pParent ? pParent->nFullName+1 : 0;
355
+ if( pChild ) pChild->isLast = 0;
356
+ while( zPath[i] ){
357
+ FileTreeNode *pNew;
358
+ int iStart = i;
359
+ int nByte;
360
+ while( zPath[i] && zPath[i]!='/' ){ i++; }
361
+ nByte = sizeof(*pNew) + i + 1;
362
+ if( zUuid!=0 && zPath[i]==0 ) nByte += UUID_SIZE+1;
363
+ pNew = fossil_malloc( nByte );
364
+ pNew->zFullName = (char*)&pNew[1];
365
+ memcpy(pNew->zFullName, zPath, i);
366
+ pNew->zFullName[i] = 0;
367
+ pNew->nFullName = i;
368
+ if( zUuid!=0 && zPath[i]==0 ){
369
+ pNew->zUuid = pNew->zFullName + i + 1;
370
+ memcpy(pNew->zUuid, zUuid, UUID_SIZE+1);
371
+ }else{
372
+ pNew->zUuid = 0;
373
+ }
374
+ pNew->zName = pNew->zFullName + iStart;
375
+ if( pTree->pLast ){
376
+ pTree->pLast->pNext = pNew;
377
+ }else{
378
+ pTree->pFirst = pNew;
379
+ }
380
+ pNew->pPrev = pTree->pLast;
381
+ pNew->pNext = 0;
382
+ pNew->pParent = pParent;
383
+ pTree->pLast = pNew;
384
+ pNew->iLevel = pParent ? pParent->iLevel+1 : 0;
385
+ pNew->isDir = zPath[i]=='/';
386
+ pNew->isLast = 1;
387
+ while( zPath[i]=='/' ){ i++; }
388
+ pParent = pNew;
389
+ }
390
+}
391
+
392
+/*
393
+** WEBPAGE: tree
394
+**
395
+** Query parameters:
396
+**
397
+** name=PATH Directory to display. Optional
398
+** ci=LABEL Show only files in this check-in. Optional.
399
+** re=REGEXP Show only files matching REGEXP. Optional.
400
+** expand Begin with the tree fully expanded.
401
+** nofiles Show directories (folders) only. Omit files.
402
+*/
403
+void page_tree(void){
404
+ char *zD = fossil_strdup(P("name"));
405
+ int nD = zD ? strlen(zD)+1 : 0;
406
+ const char *zCI = P("ci");
407
+ int rid = 0;
408
+ char *zUuid = 0;
409
+ Blob dirname;
410
+ Manifest *pM = 0;
411
+ int nFile = 0; /* Number of files (or folders with "nofiles") */
412
+ int linkTrunk = 1; /* include link to "trunk" */
413
+ int linkTip = 1; /* include link to "tip" */
414
+ const char *zRE; /* the value for the re=REGEXP query parameter */
415
+ const char *zObjType; /* "files" by default or "folders" for "nofiles" */
416
+ char *zREx = ""; /* Extra parameters for path hyperlinks */
417
+ ReCompiled *pRE = 0; /* Compiled regular expression */
418
+ FileTreeNode *p; /* One line of the tree */
419
+ FileTree sTree; /* The complete tree of files */
420
+ HQuery sURI; /* Hyperlink */
421
+ int startExpanded; /* True to start out with the tree expanded */
422
+ int showDirOnly; /* Show directories only. Omit files */
423
+ int nDir = 0; /* Number of directories. Used for ID attributes */
424
+ char *zProjectName = db_get("project-name", 0);
425
+
426
+ if( strcmp(PD("type",""),"flat")==0 ){ page_dir(); return; }
427
+ memset(&sTree, 0, sizeof(sTree));
428
+ login_check_credentials();
429
+ if( !g.perm.Read ){ login_needed(); return; }
430
+ while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
431
+ sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
432
+ pathelementFunc, 0, 0);
433
+ url_initialize(&sURI, "tree");
434
+ if( P("nofiles")!=0 ){
435
+ showDirOnly = 1;
436
+ url_add_parameter(&sURI, "nofiles", "1");
437
+ style_header("Folder Hierarchy");
438
+ }else{
439
+ showDirOnly = 0;
440
+ style_header("File Tree");
441
+ }
442
+ if( P("expand")!=0 ){
443
+ startExpanded = 1;
444
+ url_add_parameter(&sURI, "expand", "1");
445
+ }else{
446
+ startExpanded = 0;
447
+ }
448
+
449
+ /* If a regular expression is specified, compile it */
450
+ zRE = P("re");
451
+ if( zRE ){
452
+ re_compile(&pRE, zRE, 0);
453
+ url_add_parameter(&sURI, "re", zRE);
454
+ zREx = mprintf("&re=%T", zRE);
455
+ }
456
+
457
+ /* If the name= parameter is an empty string, make it a NULL pointer */
458
+ if( zD && strlen(zD)==0 ){ zD = 0; }
459
+
460
+ /* If a specific check-in is requested, fetch and parse it. If the
461
+ ** specific check-in does not exist, clear zCI. zCI==0 will cause all
462
+ ** files from all check-ins to be displayed.
463
+ */
464
+ if( zCI ){
465
+ pM = manifest_get_by_name(zCI, &rid);
466
+ if( pM ){
467
+ int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
468
+ linkTrunk = trunkRid && rid != trunkRid;
469
+ linkTip = rid != symbolic_name_to_rid("tip", "ci");
470
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
471
+ url_add_parameter(&sURI, "ci", zCI);
472
+ }else{
473
+ zCI = 0;
474
+ }
475
+ }
476
+
477
+ /* Compute the title of the page */
478
+ blob_zero(&dirname);
479
+ if( zD ){
480
+ url_add_parameter(&sURI, "name", zD);
481
+ blob_append(&dirname, "within directory ", -1);
482
+ hyperlinked_path(zD, &dirname, zCI, "tree", zREx);
483
+ if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
484
+ style_submenu_element("Top-Level", "Top-Level", "%s",
485
+ url_render(&sURI, "name", 0, 0, 0));
486
+ }else{
487
+ if( zRE ){
488
+ blob_appendf(&dirname, "matching \"%s\"", zRE);
489
+ }
490
+ }
491
+ if( zCI ){
492
+ style_submenu_element("All", "All", "%s",
493
+ url_render(&sURI, "ci", 0, 0, 0));
494
+ if( nD==0 && !showDirOnly ){
495
+ style_submenu_element("File Ages", "File Ages", "%R/fileage?name=%S",
496
+ zUuid);
497
+ }
498
+ }
499
+ if( linkTrunk ){
500
+ style_submenu_element("Trunk", "Trunk", "%s",
501
+ url_render(&sURI, "ci", "trunk", 0, 0));
502
+ }
503
+ if ( linkTip ){
504
+ style_submenu_element("Tip", "Tip", "%s",
505
+ url_render(&sURI, "ci", "tip", 0, 0));
506
+ }
507
+ if( !showDirOnly ){
508
+ style_submenu_element("Flat-View", "Flat-View", "%s",
509
+ url_render(&sURI, "type", "flat", 0, 0));
510
+ }
511
+
512
+ /* Compute the file hierarchy.
513
+ */
514
+ if( zCI ){
515
+ Stmt ins, q;
516
+ ManifestFile *pFile;
517
+
518
+ db_multi_exec(
519
+ "CREATE TEMP TABLE filelist("
520
+ " x TEXT PRIMARY KEY COLLATE nocase,"
521
+ " uuid TEXT"
522
+ ")%s;",
523
+ /* Can be removed as soon as SQLite 3.8.2 is sufficiently wide-spread */
524
+ sqlite3_libversion_number()>=3008002 ? " WITHOUT ROWID" : ""
525
+ );
526
+ db_prepare(&ins, "INSERT OR IGNORE INTO filelist VALUES(:f,:u)");
527
+ manifest_file_rewind(pM);
528
+ while( (pFile = manifest_file_next(pM,0))!=0 ){
529
+ if( nD>0
530
+ && (fossil_strncmp(pFile->zName, zD, nD-1)!=0
531
+ || pFile->zName[nD-1]!='/')
532
+ ){
533
+ continue;
534
+ }
535
+ if( pRE && re_match(pRE, (const u8*)pFile->zName, -1)==0 ) continue;
536
+ db_bind_text(&ins, ":f", pFile->zName);
537
+ db_bind_text(&ins, ":u", pFile->zUuid);
538
+ db_step(&ins);
539
+ db_reset(&ins);
540
+ }
541
+ db_finalize(&ins);
542
+ db_prepare(&q, "SELECT x, uuid FROM filelist ORDER BY x");
543
+ while( db_step(&q)==SQLITE_ROW ){
544
+ tree_add_node(&sTree, db_column_text(&q,0), db_column_text(&q,1));
545
+ nFile++;
546
+ }
547
+ db_finalize(&q);
548
+ }else{
549
+ Stmt q;
550
+ db_prepare(&q, "SELECT name FROM filename ORDER BY name COLLATE nocase");
551
+ while( db_step(&q)==SQLITE_ROW ){
552
+ const char *z = db_column_text(&q, 0);
553
+ if( nD>0 && (fossil_strncmp(z, zD, nD-1)!=0 || z[nD-1]!='/') ){
554
+ continue;
555
+ }
556
+ if( pRE && re_match(pRE, (const u8*)z, -1)==0 ) continue;
557
+ tree_add_node(&sTree, z, 0);
558
+ nFile++;
559
+ }
560
+ db_finalize(&q);
561
+ }
562
+
563
+ if( showDirOnly ){
564
+ for(nFile=0, p=sTree.pFirst; p; p=p->pNext){
565
+ if( p->isDir && p->nFullName>nD ) nFile++;
566
+ }
567
+ zObjType = "folders";
568
+ style_submenu_element("Files","Files","%s",
569
+ url_render(&sURI,"nofiles",0,0,0));
570
+ }else{
571
+ zObjType = "files";
572
+ style_submenu_element("Folders","Folders","%s",
573
+ url_render(&sURI,"nofiles","1",0,0));
574
+ }
575
+
576
+ if( zCI ){
577
+ @ <h2>%d(nFile) %s(zObjType) of check-in
578
+ if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){
579
+ @ "%h(zCI)"
580
+ }
581
+ @ [%z(href("vinfo?name=%T",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname))</h2>
582
+ }else{
583
+ int n = db_int(0, "SELECT count(*) FROM plink");
584
+ @ <h2>%d(nFile) %s(zObjType) from all %d(n) check-ins
585
+ @ %s(blob_str(&dirname))</h2>
586
+ }
587
+
588
+
589
+ /* Generate tree of lists.
590
+ **
591
+ ** Each file and directory is a list element: <li>. Files have class=file
592
+ ** and if the filename as the suffix "xyz" the file also has class=file-xyz.
593
+ ** Directories have class=dir. The directory specfied by the name= query
594
+ ** parameter (or the top-level directory if there is no name= query parameter)
595
+ ** adds class=subdir.
596
+ **
597
+ ** The <li> element for directories also contains a sublist <ul>
598
+ ** for the contents of that directory.
599
+ */
600
+ @ <div class="filetree"><ul>
601
+ if( nD ){
602
+ @ <li class="dir">
603
+ }else{
604
+ @ <li class="dir subdir">
605
+ }
606
+ @ %z(href("%s",url_render(&sURI,"name",0,0,0)))%h(zProjectName)</a>
607
+ @ <ul>
608
+ for(p=sTree.pFirst, nDir=0; p; p=p->pNext){
609
+ if( p->isDir ){
610
+ if( p->nFullName==nD-1 ){
611
+ @ <li class="dir subdir">
612
+ }else{
613
+ @ <li class="dir">
614
+ }
615
+ @ %z(href("%s",url_render(&sURI,"name",p->zFullName,0,0)))%h(p->zName)</a>
616
+ if( startExpanded || p->nFullName<=nD ){
617
+ @ <ul id="dir%d(nDir)">
618
+ }else{
619
+ @ <ul id="dir%d(nDir)" style='display:none;'>
620
+ }
621
+ nDir++;
622
+ }else if( !showDirOnly ){
623
+ char *zLink;
624
+ if( zCI ){
625
+ zLink = href("%R/artifact/%S",p->zUuid);
626
+ }else{
627
+ zLink = href("%R/finfo?name=%T",p->zFullName);
628
+ }
629
+ @ <li class="%z(fileext_class(p->zName))">%z(zLink)%h(p->zName)</a>
630
+ }
631
+ if( p->isLast ){
632
+ int nClose = p->iLevel - (p->pNext ? p->pNext->iLevel : 0);
633
+ while( nClose-- > 0 ){
634
+ @ </ul>
635
+ }
636
+ }
637
+ }
638
+ @ </ul>
639
+ @ </ul></div>
640
+ @ <script>(function(){
641
+ @ function isExpanded(ul){
642
+ @ var display = window.getComputedStyle(ul).getPropertyValue('display');
643
+ @ return display!='none';
644
+ @ }
645
+ @
646
+ @ function toggleDir(ul, useInitValue){
647
+ @ if( !useInitValue ){
648
+ @ expandMap[ul.id] = !isExpanded(ul);
649
+ @ history.replaceState(expandMap, '');
650
+ @ }
651
+ @ ul.style.display = expandMap[ul.id] ? 'block' : 'none';
652
+ @ }
653
+ @
654
+ @ function toggleAll(tree, useInitValue){
655
+ @ var lists = tree.querySelectorAll('.subdir > ul > li ul');
656
+ @ if( !useInitValue ){
657
+ @ var expand = true; /* Default action: make all sublists visible */
658
+ @ for( var i=0; lists[i]; i++ ){
659
+ @ if( isExpanded(lists[i]) ){
660
+ @ expand = false; /* Any already visible - make them all hidden */
661
+ @ break;
662
+ @ }
663
+ @ }
664
+ @ expandMap = {'*': expand};
665
+ @ history.replaceState(expandMap, '');
666
+ @ }
667
+ @ var display = expandMap['*'] ? 'block' : 'none';
668
+ @ for( var i=0; lists[i]; i++ ){
669
+ @ lists[i].style.display = display;
670
+ @ }
671
+ @ }
672
+ @
673
+ @ function checkState(){
674
+ @ expandMap = history.state || {};
675
+ @ if( expandMap['*'] ) toggleAll(outer_ul, true);
676
+ @ for( var id in expandMap ){
677
+ @ if( id!=='*' ) toggleDir(gebi(id), true);
678
+ @ }
679
+ @ }
680
+ @
681
+ @ /* No-op shim for IE9 */
682
+ @ if( !history.replaceState ) history.replaceState = function(){};
683
+ @ var outer_ul = document.querySelector('.filetree > ul');
684
+ @ var subdir = outer_ul.querySelector('.subdir');
685
+ @ var expandMap = {};
686
+ @ checkState();
687
+ @ outer_ul.onclick = function(e){
688
+ @ var a = e.target;
689
+ @ if( a.nodeName!='A' ) return true;
690
+ @ if( a.parentNode==subdir ){
691
+ @ toggleAll(outer_ul);
692
+ @ return false;
693
+ @ }
694
+ @ if( !subdir.contains(a) ) return true;
695
+ @ var ul = a.nextSibling;
696
+ @ while( ul && ul.nodeName!='UL' ) ul = ul.nextSibling;
697
+ @ if( !ul ) return true; /* This is a file link, not a directory */
698
+ @ toggleDir(ul);
699
+ @ return false;
700
+ @ }
701
+ @ }())</script>
702
+ style_footer();
703
+
704
+ /* We could free memory used by sTree here if we needed to. But
705
+ ** the process is about to exit, so doing so would not really accomplish
706
+ ** anything useful. */
707
+}
292708
293709
/*
294710
** Return a CSS class name based on the given filename's extension.
295711
** Result must be freed by the caller.
296712
**/
@@ -299,11 +715,11 @@
299715
const char *zExt = strrchr(zFilename, '.');
300716
int isExt = zExt && zExt!=zFilename && zExt[1];
301717
int i;
302718
for( i=1; isExt && zExt[i]; i++ ) isExt &= fossil_isalnum(zExt[i]);
303719
if( isExt ){
304
- zClass = mprintf("file-%s", zExt+1);
720
+ zClass = mprintf("file file-%s", zExt+1);
305721
for ( i=5; zClass[i]; i++ ) zClass[i] = fossil_tolower(zClass[i]);
306722
}else{
307723
zClass = mprintf("file");
308724
}
309725
return zClass;
@@ -404,14 +820,15 @@
404820
if( zName==0 ) zName = "tip";
405821
rid = symbolic_name_to_rid(zName, "ci");
406822
if( rid==0 ){
407823
fossil_fatal("not a valid check-in: %s", zName);
408824
}
825
+ style_submenu_element("Tree-View", "Tree-View", "%R/tree?ci=%T", zName);
409826
style_header("File Ages", zName);
410827
compute_fileage(rid);
411828
baseTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
412
- zBaseTime = db_text("","SELECT datetime(%.20g,'localtime')", baseTime);
829
+ zBaseTime = db_text("","SELECT datetime(%.20g%s)", baseTime, timeline_utc());
413830
@ <h2>File Ages For Check-in
414831
@ %z(href("%R/info?name=%T",zName))%h(zName)</a></h2>
415832
@
416833
@ <p>The times given are relative to
417834
@ %z(href("%R/timeline?c=%T",zBaseTime))%s(zBaseTime)</a>, which is the
418835
--- src/browse.c
+++ src/browse.c
@@ -71,23 +71,29 @@
71 ** There is no hyperlink on the file element of the path.
72 **
73 ** The computed string is appended to the pOut blob. pOut should
74 ** have already been initialized.
75 */
76 void hyperlinked_path(const char *zPath, Blob *pOut, const char *zCI){
 
 
 
 
 
 
77 int i, j;
78 char *zSep = "";
79
80 for(i=0; zPath[i]; i=j){
81 for(j=i; zPath[j] && zPath[j]!='/'; j++){}
82 if( zPath[j] && g.perm.Hyperlink ){
83 if( zCI ){
84 char *zLink = href("%R/dir?ci=%S&name=%#T", zCI, j, zPath);
85 blob_appendf(pOut, "%s%z%#h</a>",
86 zSep, zLink, j-i, &zPath[i]);
87 }else{
88 char *zLink = href("%R/dir?name=%#T", j, zPath);
89 blob_appendf(pOut, "%s%z%#h</a>",
90 zSep, zLink, j-i, &zPath[i]);
91 }
92 }else{
93 blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
@@ -101,11 +107,11 @@
101 /*
102 ** WEBPAGE: dir
103 **
104 ** Query parameters:
105 **
106 ** name=PATH Directory to display. Required.
107 ** ci=LABEL Show only files in this check-in. Optional.
108 */
109 void page_dir(void){
110 char *zD = fossil_strdup(P("name"));
111 int nD = zD ? strlen(zD)+1 : 0;
@@ -118,18 +124,22 @@
118 int rid = 0;
119 char *zUuid = 0;
120 Blob dirname;
121 Manifest *pM = 0;
122 const char *zSubdirLink;
123 int linkTrunk = 1, linkTip = 1;
 
 
124
 
125 login_check_credentials();
126 if( !g.perm.Read ){ login_needed(); return; }
127 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
128 style_header("File List");
129 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
130 pathelementFunc, 0, 0);
 
131
132 /* If the name= parameter is an empty string, make it a NULL pointer */
133 if( zD && strlen(zD)==0 ){ zD = 0; }
134
135 /* If a specific check-in is requested, fetch and parse it. If the
@@ -141,58 +151,57 @@
141 if( pM ){
142 int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
143 linkTrunk = trunkRid && rid != trunkRid;
144 linkTip = rid != symbolic_name_to_rid("tip", "ci");
145 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
 
146 }else{
147 zCI = 0;
148 }
149 }
150
151 /* Compute the title of the page */
152 blob_zero(&dirname);
153 if( zD ){
 
154 blob_append(&dirname, "in directory ", -1);
155 hyperlinked_path(zD, &dirname, zCI);
156 zPrefix = mprintf("%s/", zD);
157 if( linkTrunk ){
158 style_submenu_element("Trunk", "Trunk", "%R/dir?name=%t&ci=trunk",
159 zD);
160 }
161 if ( linkTip ){
162 style_submenu_element("Tip", "Tip", "%R/dir?name=%t&ci=tip", zD);
163 }
164 }else{
165 blob_append(&dirname, "in the top-level directory", -1);
166 zPrefix = "";
167 if( linkTrunk ){
168 style_submenu_element("Trunk", "Trunk", "%R/dir?ci=trunk");
169 }
170 if ( linkTip ){
171 style_submenu_element("Tip", "Tip", "%R/dir?ci=tip");
172 }
 
 
173 }
174 if( zCI ){
175 char zShort[20];
176 memcpy(zShort, zUuid, 10);
177 zShort[10] = 0;
178 @ <h2>Files of check-in [%z(href("vinfo?name=%T",zUuid))%s(zShort)</a>]
179 @ %s(blob_str(&dirname))</h2>
180 zSubdirLink = mprintf("%R/dir?ci=%S&name=%T", zUuid, zPrefix);
181 if( zD ){
182 style_submenu_element("Top", "Top", "%R/dir?ci=%S", zUuid);
183 style_submenu_element("All", "All", "%R/dir?name=%t", zD);
184 }else{
185 style_submenu_element("All", "All", "%R/dir");
186 style_submenu_element("File Ages", "File Ages", "%R/fileage?name=%S",
187 zUuid);
188 }
189 }else{
190 @ <h2>The union of all files from all check-ins
191 @ %s(blob_str(&dirname))</h2>
192 zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
193 }
 
 
 
 
194
195 /* Compute the temporary table "localfiles" containing the names
196 ** of all files and subdirectories in the zD[] directory.
197 **
198 ** Subdirectory names begin with "/". This causes them to sort
@@ -287,10 +296,417 @@
287 db_finalize(&q);
288 manifest_destroy(pM);
289 @ </ul></td></tr></table>
290 style_footer();
291 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
293 /*
294 ** Return a CSS class name based on the given filename's extension.
295 ** Result must be freed by the caller.
296 **/
@@ -299,11 +715,11 @@
299 const char *zExt = strrchr(zFilename, '.');
300 int isExt = zExt && zExt!=zFilename && zExt[1];
301 int i;
302 for( i=1; isExt && zExt[i]; i++ ) isExt &= fossil_isalnum(zExt[i]);
303 if( isExt ){
304 zClass = mprintf("file-%s", zExt+1);
305 for ( i=5; zClass[i]; i++ ) zClass[i] = fossil_tolower(zClass[i]);
306 }else{
307 zClass = mprintf("file");
308 }
309 return zClass;
@@ -404,14 +820,15 @@
404 if( zName==0 ) zName = "tip";
405 rid = symbolic_name_to_rid(zName, "ci");
406 if( rid==0 ){
407 fossil_fatal("not a valid check-in: %s", zName);
408 }
 
409 style_header("File Ages", zName);
410 compute_fileage(rid);
411 baseTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
412 zBaseTime = db_text("","SELECT datetime(%.20g,'localtime')", baseTime);
413 @ <h2>File Ages For Check-in
414 @ %z(href("%R/info?name=%T",zName))%h(zName)</a></h2>
415 @
416 @ <p>The times given are relative to
417 @ %z(href("%R/timeline?c=%T",zBaseTime))%s(zBaseTime)</a>, which is the
418
--- src/browse.c
+++ src/browse.c
@@ -71,23 +71,29 @@
71 ** There is no hyperlink on the file element of the path.
72 **
73 ** The computed string is appended to the pOut blob. pOut should
74 ** have already been initialized.
75 */
76 void hyperlinked_path(
77 const char *zPath, /* Path to render */
78 Blob *pOut, /* Write into this blob */
79 const char *zCI, /* check-in name, or NULL */
80 const char *zURI, /* "dir" or "tree" */
81 const char *zREx /* Extra query parameters */
82 ){
83 int i, j;
84 char *zSep = "";
85
86 for(i=0; zPath[i]; i=j){
87 for(j=i; zPath[j] && zPath[j]!='/'; j++){}
88 if( zPath[j] && g.perm.Hyperlink ){
89 if( zCI ){
90 char *zLink = href("%R/%s?ci=%S&name=%#T%s", zURI, zCI, j, zPath,zREx);
91 blob_appendf(pOut, "%s%z%#h</a>",
92 zSep, zLink, j-i, &zPath[i]);
93 }else{
94 char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
95 blob_appendf(pOut, "%s%z%#h</a>",
96 zSep, zLink, j-i, &zPath[i]);
97 }
98 }else{
99 blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
@@ -101,11 +107,11 @@
107 /*
108 ** WEBPAGE: dir
109 **
110 ** Query parameters:
111 **
112 ** name=PATH Directory to display. Optional. Top-level if missing
113 ** ci=LABEL Show only files in this check-in. Optional.
114 */
115 void page_dir(void){
116 char *zD = fossil_strdup(P("name"));
117 int nD = zD ? strlen(zD)+1 : 0;
@@ -118,18 +124,22 @@
124 int rid = 0;
125 char *zUuid = 0;
126 Blob dirname;
127 Manifest *pM = 0;
128 const char *zSubdirLink;
129 int linkTrunk = 1;
130 int linkTip = 1;
131 HQuery sURI;
132
133 if( strcmp(PD("type",""),"tree")==0 ){ page_tree(); return; }
134 login_check_credentials();
135 if( !g.perm.Read ){ login_needed(); return; }
136 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
137 style_header("File List");
138 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
139 pathelementFunc, 0, 0);
140 url_initialize(&sURI, "dir");
141
142 /* If the name= parameter is an empty string, make it a NULL pointer */
143 if( zD && strlen(zD)==0 ){ zD = 0; }
144
145 /* If a specific check-in is requested, fetch and parse it. If the
@@ -141,58 +151,57 @@
151 if( pM ){
152 int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
153 linkTrunk = trunkRid && rid != trunkRid;
154 linkTip = rid != symbolic_name_to_rid("tip", "ci");
155 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
156 url_add_parameter(&sURI, "ci", zCI);
157 }else{
158 zCI = 0;
159 }
160 }
161
162 /* Compute the title of the page */
163 blob_zero(&dirname);
164 if( zD ){
165 url_add_parameter(&sURI, "name", zD);
166 blob_append(&dirname, "in directory ", -1);
167 hyperlinked_path(zD, &dirname, zCI, "dir", "");
168 zPrefix = mprintf("%s/", zD);
169 style_submenu_element("Top-Level", "Top-Level", "%s",
170 url_render(&sURI, "name", 0, 0, 0));
 
 
 
 
 
171 }else{
172 blob_append(&dirname, "in the top-level directory", -1);
173 zPrefix = "";
174 }
175 if( linkTrunk ){
176 style_submenu_element("Trunk", "Trunk", "%s",
177 url_render(&sURI, "ci", "trunk", 0, 0));
178 }
179 if( linkTip ){
180 style_submenu_element("Tip", "Tip", "%s",
181 url_render(&sURI, "ci", "tip", 0, 0));
182 }
183 if( zCI ){
184 char zShort[20];
185 memcpy(zShort, zUuid, 10);
186 zShort[10] = 0;
187 @ <h2>Files of check-in [%z(href("vinfo?name=%T",zUuid))%s(zShort)</a>]
188 @ %s(blob_str(&dirname))</h2>
189 zSubdirLink = mprintf("%R/dir?ci=%S&name=%T", zUuid, zPrefix);
190 if( nD==0 ){
 
 
 
 
191 style_submenu_element("File Ages", "File Ages", "%R/fileage?name=%S",
192 zUuid);
193 }
194 }else{
195 @ <h2>The union of all files from all check-ins
196 @ %s(blob_str(&dirname))</h2>
197 zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
198 }
199 style_submenu_element("All", "All", "%s",
200 url_render(&sURI, "ci", 0, 0, 0));
201 style_submenu_element("Tree-View", "Tree-View", "%s",
202 url_render(&sURI, "type", "tree", 0, 0));
203
204 /* Compute the temporary table "localfiles" containing the names
205 ** of all files and subdirectories in the zD[] directory.
206 **
207 ** Subdirectory names begin with "/". This causes them to sort
@@ -287,10 +296,417 @@
296 db_finalize(&q);
297 manifest_destroy(pM);
298 @ </ul></td></tr></table>
299 style_footer();
300 }
301
302 /*
303 ** Objects used by the "tree" webpage.
304 */
305 typedef struct FileTreeNode FileTreeNode;
306 typedef struct FileTree FileTree;
307
308 /*
309 ** A single line of the file hierarchy
310 */
311 struct FileTreeNode {
312 FileTreeNode *pNext; /* Next line in sequence */
313 FileTreeNode *pPrev; /* Previous line */
314 FileTreeNode *pParent; /* Directory containing this line */
315 char *zName; /* Name of this entry. The "tail" */
316 char *zFullName; /* Full pathname of this entry */
317 char *zUuid; /* SHA1 hash of this file. May be NULL. */
318 unsigned nFullName; /* Length of zFullName */
319 unsigned iLevel; /* Levels of parent directories */
320 u8 isDir; /* True if there are children */
321 u8 isLast; /* True if this is the last child of its parent */
322 };
323
324 /*
325 ** A complete file hierarchy
326 */
327 struct FileTree {
328 FileTreeNode *pFirst; /* First line of the list */
329 FileTreeNode *pLast; /* Last line of the list */
330 };
331
332 /*
333 ** Add one or more new FileTreeNodes to the FileTree object so that the
334 ** leaf object zPathname is at the end of the node list
335 */
336 static void tree_add_node(
337 FileTree *pTree, /* Tree into which nodes are added */
338 const char *zPath, /* The full pathname of file to add */
339 const char *zUuid /* UUID of the file. Might be NULL. */
340 ){
341 int i;
342 FileTreeNode *pParent;
343 FileTreeNode *pChild;
344
345 pChild = pTree->pLast;
346 pParent = pChild ? pChild->pParent : 0;
347 while( pParent!=0 &&
348 ( strncmp(pParent->zFullName, zPath, pParent->nFullName)!=0
349 || zPath[pParent->nFullName]!='/' )
350 ){
351 pChild = pParent;
352 pParent = pChild->pParent;
353 }
354 i = pParent ? pParent->nFullName+1 : 0;
355 if( pChild ) pChild->isLast = 0;
356 while( zPath[i] ){
357 FileTreeNode *pNew;
358 int iStart = i;
359 int nByte;
360 while( zPath[i] && zPath[i]!='/' ){ i++; }
361 nByte = sizeof(*pNew) + i + 1;
362 if( zUuid!=0 && zPath[i]==0 ) nByte += UUID_SIZE+1;
363 pNew = fossil_malloc( nByte );
364 pNew->zFullName = (char*)&pNew[1];
365 memcpy(pNew->zFullName, zPath, i);
366 pNew->zFullName[i] = 0;
367 pNew->nFullName = i;
368 if( zUuid!=0 && zPath[i]==0 ){
369 pNew->zUuid = pNew->zFullName + i + 1;
370 memcpy(pNew->zUuid, zUuid, UUID_SIZE+1);
371 }else{
372 pNew->zUuid = 0;
373 }
374 pNew->zName = pNew->zFullName + iStart;
375 if( pTree->pLast ){
376 pTree->pLast->pNext = pNew;
377 }else{
378 pTree->pFirst = pNew;
379 }
380 pNew->pPrev = pTree->pLast;
381 pNew->pNext = 0;
382 pNew->pParent = pParent;
383 pTree->pLast = pNew;
384 pNew->iLevel = pParent ? pParent->iLevel+1 : 0;
385 pNew->isDir = zPath[i]=='/';
386 pNew->isLast = 1;
387 while( zPath[i]=='/' ){ i++; }
388 pParent = pNew;
389 }
390 }
391
392 /*
393 ** WEBPAGE: tree
394 **
395 ** Query parameters:
396 **
397 ** name=PATH Directory to display. Optional
398 ** ci=LABEL Show only files in this check-in. Optional.
399 ** re=REGEXP Show only files matching REGEXP. Optional.
400 ** expand Begin with the tree fully expanded.
401 ** nofiles Show directories (folders) only. Omit files.
402 */
403 void page_tree(void){
404 char *zD = fossil_strdup(P("name"));
405 int nD = zD ? strlen(zD)+1 : 0;
406 const char *zCI = P("ci");
407 int rid = 0;
408 char *zUuid = 0;
409 Blob dirname;
410 Manifest *pM = 0;
411 int nFile = 0; /* Number of files (or folders with "nofiles") */
412 int linkTrunk = 1; /* include link to "trunk" */
413 int linkTip = 1; /* include link to "tip" */
414 const char *zRE; /* the value for the re=REGEXP query parameter */
415 const char *zObjType; /* "files" by default or "folders" for "nofiles" */
416 char *zREx = ""; /* Extra parameters for path hyperlinks */
417 ReCompiled *pRE = 0; /* Compiled regular expression */
418 FileTreeNode *p; /* One line of the tree */
419 FileTree sTree; /* The complete tree of files */
420 HQuery sURI; /* Hyperlink */
421 int startExpanded; /* True to start out with the tree expanded */
422 int showDirOnly; /* Show directories only. Omit files */
423 int nDir = 0; /* Number of directories. Used for ID attributes */
424 char *zProjectName = db_get("project-name", 0);
425
426 if( strcmp(PD("type",""),"flat")==0 ){ page_dir(); return; }
427 memset(&sTree, 0, sizeof(sTree));
428 login_check_credentials();
429 if( !g.perm.Read ){ login_needed(); return; }
430 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
431 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
432 pathelementFunc, 0, 0);
433 url_initialize(&sURI, "tree");
434 if( P("nofiles")!=0 ){
435 showDirOnly = 1;
436 url_add_parameter(&sURI, "nofiles", "1");
437 style_header("Folder Hierarchy");
438 }else{
439 showDirOnly = 0;
440 style_header("File Tree");
441 }
442 if( P("expand")!=0 ){
443 startExpanded = 1;
444 url_add_parameter(&sURI, "expand", "1");
445 }else{
446 startExpanded = 0;
447 }
448
449 /* If a regular expression is specified, compile it */
450 zRE = P("re");
451 if( zRE ){
452 re_compile(&pRE, zRE, 0);
453 url_add_parameter(&sURI, "re", zRE);
454 zREx = mprintf("&re=%T", zRE);
455 }
456
457 /* If the name= parameter is an empty string, make it a NULL pointer */
458 if( zD && strlen(zD)==0 ){ zD = 0; }
459
460 /* If a specific check-in is requested, fetch and parse it. If the
461 ** specific check-in does not exist, clear zCI. zCI==0 will cause all
462 ** files from all check-ins to be displayed.
463 */
464 if( zCI ){
465 pM = manifest_get_by_name(zCI, &rid);
466 if( pM ){
467 int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
468 linkTrunk = trunkRid && rid != trunkRid;
469 linkTip = rid != symbolic_name_to_rid("tip", "ci");
470 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
471 url_add_parameter(&sURI, "ci", zCI);
472 }else{
473 zCI = 0;
474 }
475 }
476
477 /* Compute the title of the page */
478 blob_zero(&dirname);
479 if( zD ){
480 url_add_parameter(&sURI, "name", zD);
481 blob_append(&dirname, "within directory ", -1);
482 hyperlinked_path(zD, &dirname, zCI, "tree", zREx);
483 if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
484 style_submenu_element("Top-Level", "Top-Level", "%s",
485 url_render(&sURI, "name", 0, 0, 0));
486 }else{
487 if( zRE ){
488 blob_appendf(&dirname, "matching \"%s\"", zRE);
489 }
490 }
491 if( zCI ){
492 style_submenu_element("All", "All", "%s",
493 url_render(&sURI, "ci", 0, 0, 0));
494 if( nD==0 && !showDirOnly ){
495 style_submenu_element("File Ages", "File Ages", "%R/fileage?name=%S",
496 zUuid);
497 }
498 }
499 if( linkTrunk ){
500 style_submenu_element("Trunk", "Trunk", "%s",
501 url_render(&sURI, "ci", "trunk", 0, 0));
502 }
503 if ( linkTip ){
504 style_submenu_element("Tip", "Tip", "%s",
505 url_render(&sURI, "ci", "tip", 0, 0));
506 }
507 if( !showDirOnly ){
508 style_submenu_element("Flat-View", "Flat-View", "%s",
509 url_render(&sURI, "type", "flat", 0, 0));
510 }
511
512 /* Compute the file hierarchy.
513 */
514 if( zCI ){
515 Stmt ins, q;
516 ManifestFile *pFile;
517
518 db_multi_exec(
519 "CREATE TEMP TABLE filelist("
520 " x TEXT PRIMARY KEY COLLATE nocase,"
521 " uuid TEXT"
522 ")%s;",
523 /* Can be removed as soon as SQLite 3.8.2 is sufficiently wide-spread */
524 sqlite3_libversion_number()>=3008002 ? " WITHOUT ROWID" : ""
525 );
526 db_prepare(&ins, "INSERT OR IGNORE INTO filelist VALUES(:f,:u)");
527 manifest_file_rewind(pM);
528 while( (pFile = manifest_file_next(pM,0))!=0 ){
529 if( nD>0
530 && (fossil_strncmp(pFile->zName, zD, nD-1)!=0
531 || pFile->zName[nD-1]!='/')
532 ){
533 continue;
534 }
535 if( pRE && re_match(pRE, (const u8*)pFile->zName, -1)==0 ) continue;
536 db_bind_text(&ins, ":f", pFile->zName);
537 db_bind_text(&ins, ":u", pFile->zUuid);
538 db_step(&ins);
539 db_reset(&ins);
540 }
541 db_finalize(&ins);
542 db_prepare(&q, "SELECT x, uuid FROM filelist ORDER BY x");
543 while( db_step(&q)==SQLITE_ROW ){
544 tree_add_node(&sTree, db_column_text(&q,0), db_column_text(&q,1));
545 nFile++;
546 }
547 db_finalize(&q);
548 }else{
549 Stmt q;
550 db_prepare(&q, "SELECT name FROM filename ORDER BY name COLLATE nocase");
551 while( db_step(&q)==SQLITE_ROW ){
552 const char *z = db_column_text(&q, 0);
553 if( nD>0 && (fossil_strncmp(z, zD, nD-1)!=0 || z[nD-1]!='/') ){
554 continue;
555 }
556 if( pRE && re_match(pRE, (const u8*)z, -1)==0 ) continue;
557 tree_add_node(&sTree, z, 0);
558 nFile++;
559 }
560 db_finalize(&q);
561 }
562
563 if( showDirOnly ){
564 for(nFile=0, p=sTree.pFirst; p; p=p->pNext){
565 if( p->isDir && p->nFullName>nD ) nFile++;
566 }
567 zObjType = "folders";
568 style_submenu_element("Files","Files","%s",
569 url_render(&sURI,"nofiles",0,0,0));
570 }else{
571 zObjType = "files";
572 style_submenu_element("Folders","Folders","%s",
573 url_render(&sURI,"nofiles","1",0,0));
574 }
575
576 if( zCI ){
577 @ <h2>%d(nFile) %s(zObjType) of check-in
578 if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){
579 @ "%h(zCI)"
580 }
581 @ [%z(href("vinfo?name=%T",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname))</h2>
582 }else{
583 int n = db_int(0, "SELECT count(*) FROM plink");
584 @ <h2>%d(nFile) %s(zObjType) from all %d(n) check-ins
585 @ %s(blob_str(&dirname))</h2>
586 }
587
588
589 /* Generate tree of lists.
590 **
591 ** Each file and directory is a list element: <li>. Files have class=file
592 ** and if the filename as the suffix "xyz" the file also has class=file-xyz.
593 ** Directories have class=dir. The directory specfied by the name= query
594 ** parameter (or the top-level directory if there is no name= query parameter)
595 ** adds class=subdir.
596 **
597 ** The <li> element for directories also contains a sublist <ul>
598 ** for the contents of that directory.
599 */
600 @ <div class="filetree"><ul>
601 if( nD ){
602 @ <li class="dir">
603 }else{
604 @ <li class="dir subdir">
605 }
606 @ %z(href("%s",url_render(&sURI,"name",0,0,0)))%h(zProjectName)</a>
607 @ <ul>
608 for(p=sTree.pFirst, nDir=0; p; p=p->pNext){
609 if( p->isDir ){
610 if( p->nFullName==nD-1 ){
611 @ <li class="dir subdir">
612 }else{
613 @ <li class="dir">
614 }
615 @ %z(href("%s",url_render(&sURI,"name",p->zFullName,0,0)))%h(p->zName)</a>
616 if( startExpanded || p->nFullName<=nD ){
617 @ <ul id="dir%d(nDir)">
618 }else{
619 @ <ul id="dir%d(nDir)" style='display:none;'>
620 }
621 nDir++;
622 }else if( !showDirOnly ){
623 char *zLink;
624 if( zCI ){
625 zLink = href("%R/artifact/%S",p->zUuid);
626 }else{
627 zLink = href("%R/finfo?name=%T",p->zFullName);
628 }
629 @ <li class="%z(fileext_class(p->zName))">%z(zLink)%h(p->zName)</a>
630 }
631 if( p->isLast ){
632 int nClose = p->iLevel - (p->pNext ? p->pNext->iLevel : 0);
633 while( nClose-- > 0 ){
634 @ </ul>
635 }
636 }
637 }
638 @ </ul>
639 @ </ul></div>
640 @ <script>(function(){
641 @ function isExpanded(ul){
642 @ var display = window.getComputedStyle(ul).getPropertyValue('display');
643 @ return display!='none';
644 @ }
645 @
646 @ function toggleDir(ul, useInitValue){
647 @ if( !useInitValue ){
648 @ expandMap[ul.id] = !isExpanded(ul);
649 @ history.replaceState(expandMap, '');
650 @ }
651 @ ul.style.display = expandMap[ul.id] ? 'block' : 'none';
652 @ }
653 @
654 @ function toggleAll(tree, useInitValue){
655 @ var lists = tree.querySelectorAll('.subdir > ul > li ul');
656 @ if( !useInitValue ){
657 @ var expand = true; /* Default action: make all sublists visible */
658 @ for( var i=0; lists[i]; i++ ){
659 @ if( isExpanded(lists[i]) ){
660 @ expand = false; /* Any already visible - make them all hidden */
661 @ break;
662 @ }
663 @ }
664 @ expandMap = {'*': expand};
665 @ history.replaceState(expandMap, '');
666 @ }
667 @ var display = expandMap['*'] ? 'block' : 'none';
668 @ for( var i=0; lists[i]; i++ ){
669 @ lists[i].style.display = display;
670 @ }
671 @ }
672 @
673 @ function checkState(){
674 @ expandMap = history.state || {};
675 @ if( expandMap['*'] ) toggleAll(outer_ul, true);
676 @ for( var id in expandMap ){
677 @ if( id!=='*' ) toggleDir(gebi(id), true);
678 @ }
679 @ }
680 @
681 @ /* No-op shim for IE9 */
682 @ if( !history.replaceState ) history.replaceState = function(){};
683 @ var outer_ul = document.querySelector('.filetree > ul');
684 @ var subdir = outer_ul.querySelector('.subdir');
685 @ var expandMap = {};
686 @ checkState();
687 @ outer_ul.onclick = function(e){
688 @ var a = e.target;
689 @ if( a.nodeName!='A' ) return true;
690 @ if( a.parentNode==subdir ){
691 @ toggleAll(outer_ul);
692 @ return false;
693 @ }
694 @ if( !subdir.contains(a) ) return true;
695 @ var ul = a.nextSibling;
696 @ while( ul && ul.nodeName!='UL' ) ul = ul.nextSibling;
697 @ if( !ul ) return true; /* This is a file link, not a directory */
698 @ toggleDir(ul);
699 @ return false;
700 @ }
701 @ }())</script>
702 style_footer();
703
704 /* We could free memory used by sTree here if we needed to. But
705 ** the process is about to exit, so doing so would not really accomplish
706 ** anything useful. */
707 }
708
709 /*
710 ** Return a CSS class name based on the given filename's extension.
711 ** Result must be freed by the caller.
712 **/
@@ -299,11 +715,11 @@
715 const char *zExt = strrchr(zFilename, '.');
716 int isExt = zExt && zExt!=zFilename && zExt[1];
717 int i;
718 for( i=1; isExt && zExt[i]; i++ ) isExt &= fossil_isalnum(zExt[i]);
719 if( isExt ){
720 zClass = mprintf("file file-%s", zExt+1);
721 for ( i=5; zClass[i]; i++ ) zClass[i] = fossil_tolower(zClass[i]);
722 }else{
723 zClass = mprintf("file");
724 }
725 return zClass;
@@ -404,14 +820,15 @@
820 if( zName==0 ) zName = "tip";
821 rid = symbolic_name_to_rid(zName, "ci");
822 if( rid==0 ){
823 fossil_fatal("not a valid check-in: %s", zName);
824 }
825 style_submenu_element("Tree-View", "Tree-View", "%R/tree?ci=%T", zName);
826 style_header("File Ages", zName);
827 compute_fileage(rid);
828 baseTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
829 zBaseTime = db_text("","SELECT datetime(%.20g%s)", baseTime, timeline_utc());
830 @ <h2>File Ages For Check-in
831 @ %z(href("%R/info?name=%T",zName))%h(zName)</a></h2>
832 @
833 @ <p>The times given are relative to
834 @ %z(href("%R/timeline?c=%T",zBaseTime))%s(zBaseTime)</a>, which is the
835
+23 -8
--- src/checkin.c
+++ src/checkin.c
@@ -301,13 +301,13 @@
301301
}
302302
vfile_check_signature(vid, 0);
303303
if( showAge ){
304304
db_prepare(&q,
305305
"SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0),"
306
- " datetime(checkin_mtime(%d,rid),'unixepoch','localtime')"
306
+ " datetime(checkin_mtime(%d,rid),'unixepoch'%s)"
307307
" FROM vfile %s"
308
- " ORDER BY %s", vid, blob_str(&where), zOrderBy
308
+ " ORDER BY %s", vid, timeline_utc(), blob_str(&where), zOrderBy
309309
);
310310
}else{
311311
db_prepare(&q,
312312
"SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)"
313313
" FROM vfile %s"
@@ -817,10 +817,17 @@
817817
blob_append(&prompt,
818818
"# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
819819
"# repositories.\n"
820820
"#\n", -1
821821
);
822
+ }
823
+ if( p->integrateFlag ){
824
+ blob_append(&prompt,
825
+ "#\n"
826
+ "# All merged-in branches will be closed due to the --integrate flag\n"
827
+ "#\n", -1
828
+ );
822829
}
823830
prompt_for_user_comment(pComment, &prompt);
824831
blob_reset(&prompt);
825832
}
826833
@@ -955,10 +962,11 @@
955962
struct CheckinInfo {
956963
Blob *pComment; /* Check-in comment text */
957964
const char *zMimetype; /* Mimetype of check-in command. May be NULL */
958965
int verifyDate; /* Verify that child is younger */
959966
int closeFlag; /* Close the branch being committed */
967
+ int integrateFlag; /* Close merged-in branches */
960968
Blob *pCksum; /* Repository checksum. May be 0 */
961969
const char *zDateOvrd; /* Date override. If 0 then use 'now' */
962970
const char *zUserOvrd; /* User override. If 0 then use g.zLogin */
963971
const char *zBranch; /* Branch name. May be 0 */
964972
const char *zColor; /* One-time background color. May be 0 */
@@ -1148,11 +1156,12 @@
11481156
}
11491157
if( p->closeFlag ){
11501158
blob_appendf(pOut, "T +closed *\n");
11511159
}
11521160
db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid"
1153
- " WHERE id=-4 ORDER BY 1");
1161
+ " WHERE id %s ORDER BY 1",
1162
+ p->integrateFlag ? "IN(0,-4)" : "=(-4)");
11541163
while( db_step(&q)==SQLITE_ROW ){
11551164
const char *zIntegrateUuid = db_column_text(&q, 0);
11561165
int rid = db_column_int(&q, 1);
11571166
if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
11581167
" WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
@@ -1276,13 +1285,10 @@
12761285
}else{
12771286
if( encodingOk ){
12781287
return 0; /* We don't want encoding warnings for this file. */
12791288
}
12801289
zWarning = "Unicode";
1281
-#if !defined(_WIN32) && !defined(__CYGWIN__)
1282
- zConvert = ""; /* On Unix, we cannot easily convert Unicode files. */
1283
-#endif
12841290
zDisable = "\"encoding-glob\" setting";
12851291
}
12861292
file_relative_name(zFilename, &fname, 0);
12871293
blob_zero(&ans);
12881294
zMsg = mprintf(
@@ -1377,11 +1383,14 @@
13771383
** allowed against a closed leaf.
13781384
**
13791385
** The --private option creates a private check-in that is never synced.
13801386
** Children of private check-ins are automatically private.
13811387
**
1382
-** the --tag option applies the symbolic tag name to the check-in.
1388
+** The --tag option applies the symbolic tag name to the check-in.
1389
+**
1390
+** The --sha1sum option detects edited files by computing each file's
1391
+** SHA1 hash rather than just checking for changes to its size or mtime.
13831392
**
13841393
** Options:
13851394
** --allow-conflict allow unresolved merge conflicts
13861395
** --allow-empty allow a commit with no changes
13871396
** --allow-fork allow the commit to fork
@@ -1390,17 +1399,20 @@
13901399
** --bgcolor COLOR apply COLOR to this one check-in only
13911400
** --branch NEW-BRANCH-NAME check in to this new branch
13921401
** --branchcolor COLOR apply given COLOR to the branch
13931402
** --close close the branch being committed
13941403
** --delta use a delta manifest in the commit process
1404
+** --integrate close all merged-in branches
13951405
** -m|--comment COMMENT-TEXT use COMMENT-TEXT as commit comment
13961406
** -M|--message-file FILE read the commit comment from given file
13971407
** --mimetype MIMETYPE mimetype of check-in comment
13981408
** -n|--dry-run If given, display instead of run actions
13991409
** --no-warnings omit all warnings about file contents
14001410
** --nosign do not attempt to sign this commit with gpg
14011411
** --private do not sync changes and their descendants
1412
+** --sha1sum verify file status using SHA1 hashing rather
1413
+** than relying on file mtimes
14021414
** --tag TAG-NAME assign given tag TAG-NAME to the checkin
14031415
**
14041416
** See also: branch, changes, checkout, extra, sync
14051417
*/
14061418
void commit_cmd(void){
@@ -1410,10 +1422,11 @@
14101422
int nvid; /* Blob-id of the new check-in */
14111423
Blob comment; /* Check-in comment */
14121424
const char *zComment; /* Check-in comment */
14131425
Stmt q; /* Various queries */
14141426
char *zUuid; /* UUID of the new check-in */
1427
+ int useSha1sum = 0; /* True to verify file status using SHA1 hashing */
14151428
int noSign = 0; /* True to omit signing the manifest using GPG */
14161429
int isAMerge = 0; /* True if checking in a merge */
14171430
int noWarningFlag = 0; /* True if skipping all warnings */
14181431
int forceFlag = 0; /* Undocumented: Disables all checks */
14191432
int forceDelta = 0; /* Force a delta-manifest */
@@ -1441,10 +1454,11 @@
14411454
Blob ans;
14421455
char cReply;
14431456
14441457
memset(&sCiInfo, 0, sizeof(sCiInfo));
14451458
url_proxy_options();
1459
+ useSha1sum = find_option("sha1sum", 0, 0)!=0;
14461460
noSign = find_option("nosign",0,0)!=0;
14471461
forceDelta = find_option("delta",0,0)!=0;
14481462
forceBaseline = find_option("baseline",0,0)!=0;
14491463
if( forceDelta && forceBaseline ){
14501464
fossil_fatal("cannot use --delta and --baseline together");
@@ -1462,10 +1476,11 @@
14621476
noWarningFlag = find_option("no-warnings", 0, 0)!=0;
14631477
sCiInfo.zBranch = find_option("branch","b",1);
14641478
sCiInfo.zColor = find_option("bgcolor",0,1);
14651479
sCiInfo.zBrClr = find_option("branchcolor",0,1);
14661480
sCiInfo.closeFlag = find_option("close",0,0)!=0;
1481
+ sCiInfo.integrateFlag = find_option("integrate",0,0)!=0;
14671482
sCiInfo.zMimetype = find_option("mimetype",0,1);
14681483
while( (zTag = find_option("tag",0,1))!=0 ){
14691484
if( zTag[0]==0 ) continue;
14701485
sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag, sizeof(char*)*(nTag+2));
14711486
sCiInfo.azTag[nTag++] = zTag;
@@ -1586,11 +1601,11 @@
15861601
*/
15871602
if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
15881603
fossil_fatal("no such user: %s", g.zLogin);
15891604
}
15901605
1591
- hasChanges = unsaved_changes();
1606
+ hasChanges = unsaved_changes(useSha1sum ? CKSIG_SHA1 : 0);
15921607
db_begin_transaction();
15931608
db_record_repository_filename(0);
15941609
if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){
15951610
fossil_fatal("nothing has changed; use --allow-empty to override");
15961611
}
15971612
--- src/checkin.c
+++ src/checkin.c
@@ -301,13 +301,13 @@
301 }
302 vfile_check_signature(vid, 0);
303 if( showAge ){
304 db_prepare(&q,
305 "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0),"
306 " datetime(checkin_mtime(%d,rid),'unixepoch','localtime')"
307 " FROM vfile %s"
308 " ORDER BY %s", vid, blob_str(&where), zOrderBy
309 );
310 }else{
311 db_prepare(&q,
312 "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)"
313 " FROM vfile %s"
@@ -817,10 +817,17 @@
817 blob_append(&prompt,
818 "# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
819 "# repositories.\n"
820 "#\n", -1
821 );
 
 
 
 
 
 
 
822 }
823 prompt_for_user_comment(pComment, &prompt);
824 blob_reset(&prompt);
825 }
826
@@ -955,10 +962,11 @@
955 struct CheckinInfo {
956 Blob *pComment; /* Check-in comment text */
957 const char *zMimetype; /* Mimetype of check-in command. May be NULL */
958 int verifyDate; /* Verify that child is younger */
959 int closeFlag; /* Close the branch being committed */
 
960 Blob *pCksum; /* Repository checksum. May be 0 */
961 const char *zDateOvrd; /* Date override. If 0 then use 'now' */
962 const char *zUserOvrd; /* User override. If 0 then use g.zLogin */
963 const char *zBranch; /* Branch name. May be 0 */
964 const char *zColor; /* One-time background color. May be 0 */
@@ -1148,11 +1156,12 @@
1148 }
1149 if( p->closeFlag ){
1150 blob_appendf(pOut, "T +closed *\n");
1151 }
1152 db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid"
1153 " WHERE id=-4 ORDER BY 1");
 
1154 while( db_step(&q)==SQLITE_ROW ){
1155 const char *zIntegrateUuid = db_column_text(&q, 0);
1156 int rid = db_column_int(&q, 1);
1157 if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
1158 " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
@@ -1276,13 +1285,10 @@
1276 }else{
1277 if( encodingOk ){
1278 return 0; /* We don't want encoding warnings for this file. */
1279 }
1280 zWarning = "Unicode";
1281 #if !defined(_WIN32) && !defined(__CYGWIN__)
1282 zConvert = ""; /* On Unix, we cannot easily convert Unicode files. */
1283 #endif
1284 zDisable = "\"encoding-glob\" setting";
1285 }
1286 file_relative_name(zFilename, &fname, 0);
1287 blob_zero(&ans);
1288 zMsg = mprintf(
@@ -1377,11 +1383,14 @@
1377 ** allowed against a closed leaf.
1378 **
1379 ** The --private option creates a private check-in that is never synced.
1380 ** Children of private check-ins are automatically private.
1381 **
1382 ** the --tag option applies the symbolic tag name to the check-in.
 
 
 
1383 **
1384 ** Options:
1385 ** --allow-conflict allow unresolved merge conflicts
1386 ** --allow-empty allow a commit with no changes
1387 ** --allow-fork allow the commit to fork
@@ -1390,17 +1399,20 @@
1390 ** --bgcolor COLOR apply COLOR to this one check-in only
1391 ** --branch NEW-BRANCH-NAME check in to this new branch
1392 ** --branchcolor COLOR apply given COLOR to the branch
1393 ** --close close the branch being committed
1394 ** --delta use a delta manifest in the commit process
 
1395 ** -m|--comment COMMENT-TEXT use COMMENT-TEXT as commit comment
1396 ** -M|--message-file FILE read the commit comment from given file
1397 ** --mimetype MIMETYPE mimetype of check-in comment
1398 ** -n|--dry-run If given, display instead of run actions
1399 ** --no-warnings omit all warnings about file contents
1400 ** --nosign do not attempt to sign this commit with gpg
1401 ** --private do not sync changes and their descendants
 
 
1402 ** --tag TAG-NAME assign given tag TAG-NAME to the checkin
1403 **
1404 ** See also: branch, changes, checkout, extra, sync
1405 */
1406 void commit_cmd(void){
@@ -1410,10 +1422,11 @@
1410 int nvid; /* Blob-id of the new check-in */
1411 Blob comment; /* Check-in comment */
1412 const char *zComment; /* Check-in comment */
1413 Stmt q; /* Various queries */
1414 char *zUuid; /* UUID of the new check-in */
 
1415 int noSign = 0; /* True to omit signing the manifest using GPG */
1416 int isAMerge = 0; /* True if checking in a merge */
1417 int noWarningFlag = 0; /* True if skipping all warnings */
1418 int forceFlag = 0; /* Undocumented: Disables all checks */
1419 int forceDelta = 0; /* Force a delta-manifest */
@@ -1441,10 +1454,11 @@
1441 Blob ans;
1442 char cReply;
1443
1444 memset(&sCiInfo, 0, sizeof(sCiInfo));
1445 url_proxy_options();
 
1446 noSign = find_option("nosign",0,0)!=0;
1447 forceDelta = find_option("delta",0,0)!=0;
1448 forceBaseline = find_option("baseline",0,0)!=0;
1449 if( forceDelta && forceBaseline ){
1450 fossil_fatal("cannot use --delta and --baseline together");
@@ -1462,10 +1476,11 @@
1462 noWarningFlag = find_option("no-warnings", 0, 0)!=0;
1463 sCiInfo.zBranch = find_option("branch","b",1);
1464 sCiInfo.zColor = find_option("bgcolor",0,1);
1465 sCiInfo.zBrClr = find_option("branchcolor",0,1);
1466 sCiInfo.closeFlag = find_option("close",0,0)!=0;
 
1467 sCiInfo.zMimetype = find_option("mimetype",0,1);
1468 while( (zTag = find_option("tag",0,1))!=0 ){
1469 if( zTag[0]==0 ) continue;
1470 sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag, sizeof(char*)*(nTag+2));
1471 sCiInfo.azTag[nTag++] = zTag;
@@ -1586,11 +1601,11 @@
1586 */
1587 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
1588 fossil_fatal("no such user: %s", g.zLogin);
1589 }
1590
1591 hasChanges = unsaved_changes();
1592 db_begin_transaction();
1593 db_record_repository_filename(0);
1594 if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){
1595 fossil_fatal("nothing has changed; use --allow-empty to override");
1596 }
1597
--- src/checkin.c
+++ src/checkin.c
@@ -301,13 +301,13 @@
301 }
302 vfile_check_signature(vid, 0);
303 if( showAge ){
304 db_prepare(&q,
305 "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0),"
306 " datetime(checkin_mtime(%d,rid),'unixepoch'%s)"
307 " FROM vfile %s"
308 " ORDER BY %s", vid, timeline_utc(), blob_str(&where), zOrderBy
309 );
310 }else{
311 db_prepare(&q,
312 "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)"
313 " FROM vfile %s"
@@ -817,10 +817,17 @@
817 blob_append(&prompt,
818 "# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
819 "# repositories.\n"
820 "#\n", -1
821 );
822 }
823 if( p->integrateFlag ){
824 blob_append(&prompt,
825 "#\n"
826 "# All merged-in branches will be closed due to the --integrate flag\n"
827 "#\n", -1
828 );
829 }
830 prompt_for_user_comment(pComment, &prompt);
831 blob_reset(&prompt);
832 }
833
@@ -955,10 +962,11 @@
962 struct CheckinInfo {
963 Blob *pComment; /* Check-in comment text */
964 const char *zMimetype; /* Mimetype of check-in command. May be NULL */
965 int verifyDate; /* Verify that child is younger */
966 int closeFlag; /* Close the branch being committed */
967 int integrateFlag; /* Close merged-in branches */
968 Blob *pCksum; /* Repository checksum. May be 0 */
969 const char *zDateOvrd; /* Date override. If 0 then use 'now' */
970 const char *zUserOvrd; /* User override. If 0 then use g.zLogin */
971 const char *zBranch; /* Branch name. May be 0 */
972 const char *zColor; /* One-time background color. May be 0 */
@@ -1148,11 +1156,12 @@
1156 }
1157 if( p->closeFlag ){
1158 blob_appendf(pOut, "T +closed *\n");
1159 }
1160 db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid"
1161 " WHERE id %s ORDER BY 1",
1162 p->integrateFlag ? "IN(0,-4)" : "=(-4)");
1163 while( db_step(&q)==SQLITE_ROW ){
1164 const char *zIntegrateUuid = db_column_text(&q, 0);
1165 int rid = db_column_int(&q, 1);
1166 if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
1167 " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
@@ -1276,13 +1285,10 @@
1285 }else{
1286 if( encodingOk ){
1287 return 0; /* We don't want encoding warnings for this file. */
1288 }
1289 zWarning = "Unicode";
 
 
 
1290 zDisable = "\"encoding-glob\" setting";
1291 }
1292 file_relative_name(zFilename, &fname, 0);
1293 blob_zero(&ans);
1294 zMsg = mprintf(
@@ -1377,11 +1383,14 @@
1383 ** allowed against a closed leaf.
1384 **
1385 ** The --private option creates a private check-in that is never synced.
1386 ** Children of private check-ins are automatically private.
1387 **
1388 ** The --tag option applies the symbolic tag name to the check-in.
1389 **
1390 ** The --sha1sum option detects edited files by computing each file's
1391 ** SHA1 hash rather than just checking for changes to its size or mtime.
1392 **
1393 ** Options:
1394 ** --allow-conflict allow unresolved merge conflicts
1395 ** --allow-empty allow a commit with no changes
1396 ** --allow-fork allow the commit to fork
@@ -1390,17 +1399,20 @@
1399 ** --bgcolor COLOR apply COLOR to this one check-in only
1400 ** --branch NEW-BRANCH-NAME check in to this new branch
1401 ** --branchcolor COLOR apply given COLOR to the branch
1402 ** --close close the branch being committed
1403 ** --delta use a delta manifest in the commit process
1404 ** --integrate close all merged-in branches
1405 ** -m|--comment COMMENT-TEXT use COMMENT-TEXT as commit comment
1406 ** -M|--message-file FILE read the commit comment from given file
1407 ** --mimetype MIMETYPE mimetype of check-in comment
1408 ** -n|--dry-run If given, display instead of run actions
1409 ** --no-warnings omit all warnings about file contents
1410 ** --nosign do not attempt to sign this commit with gpg
1411 ** --private do not sync changes and their descendants
1412 ** --sha1sum verify file status using SHA1 hashing rather
1413 ** than relying on file mtimes
1414 ** --tag TAG-NAME assign given tag TAG-NAME to the checkin
1415 **
1416 ** See also: branch, changes, checkout, extra, sync
1417 */
1418 void commit_cmd(void){
@@ -1410,10 +1422,11 @@
1422 int nvid; /* Blob-id of the new check-in */
1423 Blob comment; /* Check-in comment */
1424 const char *zComment; /* Check-in comment */
1425 Stmt q; /* Various queries */
1426 char *zUuid; /* UUID of the new check-in */
1427 int useSha1sum = 0; /* True to verify file status using SHA1 hashing */
1428 int noSign = 0; /* True to omit signing the manifest using GPG */
1429 int isAMerge = 0; /* True if checking in a merge */
1430 int noWarningFlag = 0; /* True if skipping all warnings */
1431 int forceFlag = 0; /* Undocumented: Disables all checks */
1432 int forceDelta = 0; /* Force a delta-manifest */
@@ -1441,10 +1454,11 @@
1454 Blob ans;
1455 char cReply;
1456
1457 memset(&sCiInfo, 0, sizeof(sCiInfo));
1458 url_proxy_options();
1459 useSha1sum = find_option("sha1sum", 0, 0)!=0;
1460 noSign = find_option("nosign",0,0)!=0;
1461 forceDelta = find_option("delta",0,0)!=0;
1462 forceBaseline = find_option("baseline",0,0)!=0;
1463 if( forceDelta && forceBaseline ){
1464 fossil_fatal("cannot use --delta and --baseline together");
@@ -1462,10 +1476,11 @@
1476 noWarningFlag = find_option("no-warnings", 0, 0)!=0;
1477 sCiInfo.zBranch = find_option("branch","b",1);
1478 sCiInfo.zColor = find_option("bgcolor",0,1);
1479 sCiInfo.zBrClr = find_option("branchcolor",0,1);
1480 sCiInfo.closeFlag = find_option("close",0,0)!=0;
1481 sCiInfo.integrateFlag = find_option("integrate",0,0)!=0;
1482 sCiInfo.zMimetype = find_option("mimetype",0,1);
1483 while( (zTag = find_option("tag",0,1))!=0 ){
1484 if( zTag[0]==0 ) continue;
1485 sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag, sizeof(char*)*(nTag+2));
1486 sCiInfo.azTag[nTag++] = zTag;
@@ -1586,11 +1601,11 @@
1601 */
1602 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
1603 fossil_fatal("no such user: %s", g.zLogin);
1604 }
1605
1606 hasChanges = unsaved_changes(useSha1sum ? CKSIG_SHA1 : 0);
1607 db_begin_transaction();
1608 db_record_repository_filename(0);
1609 if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){
1610 fossil_fatal("nothing has changed; use --allow-empty to override");
1611 }
1612
+4 -4
--- src/checkout.c
+++ src/checkout.c
@@ -28,16 +28,16 @@
2828
**
2929
** 0: There is an existing checkout but it is unmodified
3030
** 1: There is a modified checkout - there are unsaved changes
3131
** 2: There is no existing checkout
3232
*/
33
-int unsaved_changes(void){
33
+int unsaved_changes(unsigned int cksigFlags){
3434
int vid;
3535
db_must_be_within_tree();
3636
vid = db_lget_int("checkout",0);
3737
if( vid==0 ) return 2;
38
- vfile_check_signature(vid, CKSIG_ENOTFILE);
38
+ vfile_check_signature(vid, cksigFlags|CKSIG_ENOTFILE);
3939
return db_exists("SELECT 1 FROM vfile WHERE chnged"
4040
" OR coalesce(origname!=pathname,0)");
4141
}
4242
4343
/*
@@ -200,11 +200,11 @@
200200
latestFlag = find_option("latest",0,0)!=0;
201201
promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
202202
if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
203203
usage("VERSION|--latest ?--force? ?--keep?");
204204
}
205
- if( !forceFlag && unsaved_changes()==1 ){
205
+ if( !forceFlag && unsaved_changes(0)==1 ){
206206
fossil_fatal("there are unsaved changes in the current checkout");
207207
}
208208
if( forceFlag ){
209209
db_multi_exec("DELETE FROM vfile");
210210
prior = 0;
@@ -288,11 +288,11 @@
288288
** See also: open
289289
*/
290290
void close_cmd(void){
291291
int forceFlag = find_option("force","f",0)!=0;
292292
db_must_be_within_tree();
293
- if( !forceFlag && unsaved_changes()==1 ){
293
+ if( !forceFlag && unsaved_changes(0)==1 ){
294294
fossil_fatal("there are unsaved changes in the current checkout");
295295
}
296296
if( !forceFlag
297297
&& db_exists("SELECT 1 FROM %s.sqlite_master WHERE name='stash'",
298298
db_name("localdb"))
299299
--- src/checkout.c
+++ src/checkout.c
@@ -28,16 +28,16 @@
28 **
29 ** 0: There is an existing checkout but it is unmodified
30 ** 1: There is a modified checkout - there are unsaved changes
31 ** 2: There is no existing checkout
32 */
33 int unsaved_changes(void){
34 int vid;
35 db_must_be_within_tree();
36 vid = db_lget_int("checkout",0);
37 if( vid==0 ) return 2;
38 vfile_check_signature(vid, CKSIG_ENOTFILE);
39 return db_exists("SELECT 1 FROM vfile WHERE chnged"
40 " OR coalesce(origname!=pathname,0)");
41 }
42
43 /*
@@ -200,11 +200,11 @@
200 latestFlag = find_option("latest",0,0)!=0;
201 promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
202 if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
203 usage("VERSION|--latest ?--force? ?--keep?");
204 }
205 if( !forceFlag && unsaved_changes()==1 ){
206 fossil_fatal("there are unsaved changes in the current checkout");
207 }
208 if( forceFlag ){
209 db_multi_exec("DELETE FROM vfile");
210 prior = 0;
@@ -288,11 +288,11 @@
288 ** See also: open
289 */
290 void close_cmd(void){
291 int forceFlag = find_option("force","f",0)!=0;
292 db_must_be_within_tree();
293 if( !forceFlag && unsaved_changes()==1 ){
294 fossil_fatal("there are unsaved changes in the current checkout");
295 }
296 if( !forceFlag
297 && db_exists("SELECT 1 FROM %s.sqlite_master WHERE name='stash'",
298 db_name("localdb"))
299
--- src/checkout.c
+++ src/checkout.c
@@ -28,16 +28,16 @@
28 **
29 ** 0: There is an existing checkout but it is unmodified
30 ** 1: There is a modified checkout - there are unsaved changes
31 ** 2: There is no existing checkout
32 */
33 int unsaved_changes(unsigned int cksigFlags){
34 int vid;
35 db_must_be_within_tree();
36 vid = db_lget_int("checkout",0);
37 if( vid==0 ) return 2;
38 vfile_check_signature(vid, cksigFlags|CKSIG_ENOTFILE);
39 return db_exists("SELECT 1 FROM vfile WHERE chnged"
40 " OR coalesce(origname!=pathname,0)");
41 }
42
43 /*
@@ -200,11 +200,11 @@
200 latestFlag = find_option("latest",0,0)!=0;
201 promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
202 if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
203 usage("VERSION|--latest ?--force? ?--keep?");
204 }
205 if( !forceFlag && unsaved_changes(0)==1 ){
206 fossil_fatal("there are unsaved changes in the current checkout");
207 }
208 if( forceFlag ){
209 db_multi_exec("DELETE FROM vfile");
210 prior = 0;
@@ -288,11 +288,11 @@
288 ** See also: open
289 */
290 void close_cmd(void){
291 int forceFlag = find_option("force","f",0)!=0;
292 db_must_be_within_tree();
293 if( !forceFlag && unsaved_changes(0)==1 ){
294 fossil_fatal("there are unsaved changes in the current checkout");
295 }
296 if( !forceFlag
297 && db_exists("SELECT 1 FROM %s.sqlite_master WHERE name='stash'",
298 db_name("localdb"))
299
+7 -4
--- src/db.c
+++ src/db.c
@@ -315,10 +315,14 @@
315315
return sqlite3_bind_double(pStmt->pStmt, paramIdx(pStmt, zParamName), rValue);
316316
}
317317
int db_bind_text(Stmt *pStmt, const char *zParamName, const char *zValue){
318318
return sqlite3_bind_text(pStmt->pStmt, paramIdx(pStmt, zParamName), zValue,
319319
-1, SQLITE_STATIC);
320
+}
321
+int db_bind_text16(Stmt *pStmt, const char *zParamName, const char *zValue){
322
+ return sqlite3_bind_text16(pStmt->pStmt, paramIdx(pStmt, zParamName), zValue,
323
+ -1, SQLITE_STATIC);
320324
}
321325
int db_bind_null(Stmt *pStmt, const char *zParamName){
322326
return sqlite3_bind_null(pStmt->pStmt, paramIdx(pStmt, zParamName));
323327
}
324328
int db_bind_blob(Stmt *pStmt, const char *zParamName, Blob *pBlob){
@@ -942,18 +946,17 @@
942946
static const char aDbName[][10] = { "_FOSSIL_", ".fslckout", ".fos" };
943947
944948
if( g.localOpen) return 1;
945949
file_getcwd(zPwd, sizeof(zPwd)-20);
946950
n = strlen(zPwd);
947
- if( n==1 && zPwd[0]=='/' ) zPwd[0] = '.';
948951
while( n>0 ){
949952
for(i=0; i<count(aDbName); i++){
950953
sqlite3_snprintf(sizeof(zPwd)-n, &zPwd[n], "/%s", aDbName[i]);
951954
if( isValidLocalDb(zPwd) ){
952955
/* Found a valid checkout database file */
953956
zPwd[n] = 0;
954
- while( n>1 && zPwd[n-1]=='/' ){
957
+ while( n>0 && zPwd[n-1]=='/' ){
955958
n--;
956959
zPwd[n] = 0;
957960
}
958961
g.zLocalRoot = mprintf("%s/", zPwd);
959962
g.localOpen = 1;
@@ -961,12 +964,12 @@
961964
db_open_repository(zDbName);
962965
return 1;
963966
}
964967
}
965968
n--;
966
- while( n>0 && zPwd[n]!='/' ){ n--; }
967
- while( n>0 && zPwd[n-1]=='/' ){ n--; }
969
+ while( n>1 && zPwd[n]!='/' ){ n--; }
970
+ while( n>1 && zPwd[n-1]=='/' ){ n--; }
968971
zPwd[n] = 0;
969972
}
970973
971974
/* A checkout database file could not be found */
972975
return 0;
973976
--- src/db.c
+++ src/db.c
@@ -315,10 +315,14 @@
315 return sqlite3_bind_double(pStmt->pStmt, paramIdx(pStmt, zParamName), rValue);
316 }
317 int db_bind_text(Stmt *pStmt, const char *zParamName, const char *zValue){
318 return sqlite3_bind_text(pStmt->pStmt, paramIdx(pStmt, zParamName), zValue,
319 -1, SQLITE_STATIC);
 
 
 
 
320 }
321 int db_bind_null(Stmt *pStmt, const char *zParamName){
322 return sqlite3_bind_null(pStmt->pStmt, paramIdx(pStmt, zParamName));
323 }
324 int db_bind_blob(Stmt *pStmt, const char *zParamName, Blob *pBlob){
@@ -942,18 +946,17 @@
942 static const char aDbName[][10] = { "_FOSSIL_", ".fslckout", ".fos" };
943
944 if( g.localOpen) return 1;
945 file_getcwd(zPwd, sizeof(zPwd)-20);
946 n = strlen(zPwd);
947 if( n==1 && zPwd[0]=='/' ) zPwd[0] = '.';
948 while( n>0 ){
949 for(i=0; i<count(aDbName); i++){
950 sqlite3_snprintf(sizeof(zPwd)-n, &zPwd[n], "/%s", aDbName[i]);
951 if( isValidLocalDb(zPwd) ){
952 /* Found a valid checkout database file */
953 zPwd[n] = 0;
954 while( n>1 && zPwd[n-1]=='/' ){
955 n--;
956 zPwd[n] = 0;
957 }
958 g.zLocalRoot = mprintf("%s/", zPwd);
959 g.localOpen = 1;
@@ -961,12 +964,12 @@
961 db_open_repository(zDbName);
962 return 1;
963 }
964 }
965 n--;
966 while( n>0 && zPwd[n]!='/' ){ n--; }
967 while( n>0 && zPwd[n-1]=='/' ){ n--; }
968 zPwd[n] = 0;
969 }
970
971 /* A checkout database file could not be found */
972 return 0;
973
--- src/db.c
+++ src/db.c
@@ -315,10 +315,14 @@
315 return sqlite3_bind_double(pStmt->pStmt, paramIdx(pStmt, zParamName), rValue);
316 }
317 int db_bind_text(Stmt *pStmt, const char *zParamName, const char *zValue){
318 return sqlite3_bind_text(pStmt->pStmt, paramIdx(pStmt, zParamName), zValue,
319 -1, SQLITE_STATIC);
320 }
321 int db_bind_text16(Stmt *pStmt, const char *zParamName, const char *zValue){
322 return sqlite3_bind_text16(pStmt->pStmt, paramIdx(pStmt, zParamName), zValue,
323 -1, SQLITE_STATIC);
324 }
325 int db_bind_null(Stmt *pStmt, const char *zParamName){
326 return sqlite3_bind_null(pStmt->pStmt, paramIdx(pStmt, zParamName));
327 }
328 int db_bind_blob(Stmt *pStmt, const char *zParamName, Blob *pBlob){
@@ -942,18 +946,17 @@
946 static const char aDbName[][10] = { "_FOSSIL_", ".fslckout", ".fos" };
947
948 if( g.localOpen) return 1;
949 file_getcwd(zPwd, sizeof(zPwd)-20);
950 n = strlen(zPwd);
 
951 while( n>0 ){
952 for(i=0; i<count(aDbName); i++){
953 sqlite3_snprintf(sizeof(zPwd)-n, &zPwd[n], "/%s", aDbName[i]);
954 if( isValidLocalDb(zPwd) ){
955 /* Found a valid checkout database file */
956 zPwd[n] = 0;
957 while( n>0 && zPwd[n-1]=='/' ){
958 n--;
959 zPwd[n] = 0;
960 }
961 g.zLocalRoot = mprintf("%s/", zPwd);
962 g.localOpen = 1;
@@ -961,12 +964,12 @@
964 db_open_repository(zDbName);
965 return 1;
966 }
967 }
968 n--;
969 while( n>1 && zPwd[n]!='/' ){ n--; }
970 while( n>1 && zPwd[n-1]=='/' ){ n--; }
971 zPwd[n] = 0;
972 }
973
974 /* A checkout database file could not be found */
975 return 0;
976
+9 -7
--- src/diff.c
+++ src/diff.c
@@ -803,11 +803,11 @@
803803
p->zStart = zClassChng;
804804
}
805805
p->iStart2 = nPrefix + aLCS[1];
806806
p->iEnd2 = nLeft - nSuffix;
807807
p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng;
808
- sbsSimplifyLine(p, zLeft+nPrefix);
808
+ sbsSimplifyLine(p, zLeft);
809809
sbsWriteText(p, pLeft, SBS_TXTA);
810810
sbsWriteMarker(p, " | ", "|");
811811
sbsWriteLineno(p, lnRight, SBS_LNB);
812812
p->iStart = nPrefix;
813813
p->iEnd = nPrefix + aLCS[2];
@@ -818,11 +818,11 @@
818818
p->zStart = zClassChng;
819819
}
820820
p->iStart2 = nPrefix + aLCS[3];
821821
p->iEnd2 = nRight - nSuffix;
822822
p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng;
823
- sbsSimplifyLine(p, zRight+nPrefix);
823
+ sbsSimplifyLine(p, zRight);
824824
sbsWriteText(p, pRight, SBS_TXTB);
825825
return;
826826
}
827827
828828
/* If all else fails, show a single big change between left and right */
@@ -1762,10 +1762,12 @@
17621762
Blob *pTemp = pA_Blob;
17631763
pA_Blob = pB_Blob;
17641764
pB_Blob = pTemp;
17651765
}
17661766
ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
1767
+ blob_to_utf8_no_bom(pA_Blob, 0);
1768
+ blob_to_utf8_no_bom(pB_Blob, 0);
17671769
17681770
/* Prepare the input files */
17691771
memset(&c, 0, sizeof(c));
17701772
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
17711773
&c.nFrom, ignoreEolWs);
@@ -2179,26 +2181,26 @@
21792181
url_add_parameter(&url, "limit", sqlite3_mprintf("%d", iLimit));
21802182
}
21812183
url_add_parameter(&url, "log", showLog ? "1" : "0");
21822184
if( showLog ){
21832185
style_submenu_element("Hide Log", "Hide Log",
2184
- url_render(&url, "log", "0", 0, 0));
2186
+ "%s", url_render(&url, "log", "0", 0, 0));
21852187
}else{
21862188
style_submenu_element("Show Log", "Show Log",
2187
- url_render(&url, "log", "1", 0, 0));
2189
+ "%s", url_render(&url, "log", "1", 0, 0));
21882190
}
21892191
if( ann.bLimit ){
21902192
char *z1, *z2;
21912193
style_submenu_element("All Ancestors", "All Ancestors",
2192
- url_render(&url, "limit", "-1", 0, 0));
2194
+ "%s", url_render(&url, "limit", "-1", 0, 0));
21932195
z1 = sqlite3_mprintf("%d Ancestors", iLimit+20);
21942196
z2 = sqlite3_mprintf("%d", iLimit+20);
2195
- style_submenu_element(z1, z1, url_render(&url, "limit", z2, 0, 0));
2197
+ style_submenu_element(z1, z1, "%s", url_render(&url, "limit", z2, 0, 0));
21962198
}
21972199
if( iLimit>20 ){
21982200
style_submenu_element("20 Ancestors", "20 Ancestors",
2199
- url_render(&url, "limit", "20", 0, 0));
2201
+ "%s", url_render(&url, "limit", "20", 0, 0));
22002202
}
22012203
if( db_get_boolean("white-foreground", 0) ){
22022204
clr1 = 0xa04040;
22032205
clr2 = 0x4059a0;
22042206
}else{
22052207
--- src/diff.c
+++ src/diff.c
@@ -803,11 +803,11 @@
803 p->zStart = zClassChng;
804 }
805 p->iStart2 = nPrefix + aLCS[1];
806 p->iEnd2 = nLeft - nSuffix;
807 p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng;
808 sbsSimplifyLine(p, zLeft+nPrefix);
809 sbsWriteText(p, pLeft, SBS_TXTA);
810 sbsWriteMarker(p, " | ", "|");
811 sbsWriteLineno(p, lnRight, SBS_LNB);
812 p->iStart = nPrefix;
813 p->iEnd = nPrefix + aLCS[2];
@@ -818,11 +818,11 @@
818 p->zStart = zClassChng;
819 }
820 p->iStart2 = nPrefix + aLCS[3];
821 p->iEnd2 = nRight - nSuffix;
822 p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng;
823 sbsSimplifyLine(p, zRight+nPrefix);
824 sbsWriteText(p, pRight, SBS_TXTB);
825 return;
826 }
827
828 /* If all else fails, show a single big change between left and right */
@@ -1762,10 +1762,12 @@
1762 Blob *pTemp = pA_Blob;
1763 pA_Blob = pB_Blob;
1764 pB_Blob = pTemp;
1765 }
1766 ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
 
 
1767
1768 /* Prepare the input files */
1769 memset(&c, 0, sizeof(c));
1770 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1771 &c.nFrom, ignoreEolWs);
@@ -2179,26 +2181,26 @@
2179 url_add_parameter(&url, "limit", sqlite3_mprintf("%d", iLimit));
2180 }
2181 url_add_parameter(&url, "log", showLog ? "1" : "0");
2182 if( showLog ){
2183 style_submenu_element("Hide Log", "Hide Log",
2184 url_render(&url, "log", "0", 0, 0));
2185 }else{
2186 style_submenu_element("Show Log", "Show Log",
2187 url_render(&url, "log", "1", 0, 0));
2188 }
2189 if( ann.bLimit ){
2190 char *z1, *z2;
2191 style_submenu_element("All Ancestors", "All Ancestors",
2192 url_render(&url, "limit", "-1", 0, 0));
2193 z1 = sqlite3_mprintf("%d Ancestors", iLimit+20);
2194 z2 = sqlite3_mprintf("%d", iLimit+20);
2195 style_submenu_element(z1, z1, url_render(&url, "limit", z2, 0, 0));
2196 }
2197 if( iLimit>20 ){
2198 style_submenu_element("20 Ancestors", "20 Ancestors",
2199 url_render(&url, "limit", "20", 0, 0));
2200 }
2201 if( db_get_boolean("white-foreground", 0) ){
2202 clr1 = 0xa04040;
2203 clr2 = 0x4059a0;
2204 }else{
2205
--- src/diff.c
+++ src/diff.c
@@ -803,11 +803,11 @@
803 p->zStart = zClassChng;
804 }
805 p->iStart2 = nPrefix + aLCS[1];
806 p->iEnd2 = nLeft - nSuffix;
807 p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng;
808 sbsSimplifyLine(p, zLeft);
809 sbsWriteText(p, pLeft, SBS_TXTA);
810 sbsWriteMarker(p, " | ", "|");
811 sbsWriteLineno(p, lnRight, SBS_LNB);
812 p->iStart = nPrefix;
813 p->iEnd = nPrefix + aLCS[2];
@@ -818,11 +818,11 @@
818 p->zStart = zClassChng;
819 }
820 p->iStart2 = nPrefix + aLCS[3];
821 p->iEnd2 = nRight - nSuffix;
822 p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng;
823 sbsSimplifyLine(p, zRight);
824 sbsWriteText(p, pRight, SBS_TXTB);
825 return;
826 }
827
828 /* If all else fails, show a single big change between left and right */
@@ -1762,10 +1762,12 @@
1762 Blob *pTemp = pA_Blob;
1763 pA_Blob = pB_Blob;
1764 pB_Blob = pTemp;
1765 }
1766 ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
1767 blob_to_utf8_no_bom(pA_Blob, 0);
1768 blob_to_utf8_no_bom(pB_Blob, 0);
1769
1770 /* Prepare the input files */
1771 memset(&c, 0, sizeof(c));
1772 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1773 &c.nFrom, ignoreEolWs);
@@ -2179,26 +2181,26 @@
2181 url_add_parameter(&url, "limit", sqlite3_mprintf("%d", iLimit));
2182 }
2183 url_add_parameter(&url, "log", showLog ? "1" : "0");
2184 if( showLog ){
2185 style_submenu_element("Hide Log", "Hide Log",
2186 "%s", url_render(&url, "log", "0", 0, 0));
2187 }else{
2188 style_submenu_element("Show Log", "Show Log",
2189 "%s", url_render(&url, "log", "1", 0, 0));
2190 }
2191 if( ann.bLimit ){
2192 char *z1, *z2;
2193 style_submenu_element("All Ancestors", "All Ancestors",
2194 "%s", url_render(&url, "limit", "-1", 0, 0));
2195 z1 = sqlite3_mprintf("%d Ancestors", iLimit+20);
2196 z2 = sqlite3_mprintf("%d", iLimit+20);
2197 style_submenu_element(z1, z1, "%s", url_render(&url, "limit", z2, 0, 0));
2198 }
2199 if( iLimit>20 ){
2200 style_submenu_element("20 Ancestors", "20 Ancestors",
2201 "%s", url_render(&url, "limit", "20", 0, 0));
2202 }
2203 if( db_get_boolean("white-foreground", 0) ){
2204 clr1 = 0xa04040;
2205 clr2 = 0x4059a0;
2206 }else{
2207
+1
--- src/doc.c
+++ src/doc.c
@@ -483,10 +483,11 @@
483483
if( content_get(rid, &filebody)==0 ){
484484
goto doc_not_found;
485485
}
486486
db_end_transaction(0);
487487
}
488
+ blob_to_utf8_no_bom(&filebody, 0);
488489
489490
/* The file is now contained in the filebody blob. Deliver the
490491
** file to the user
491492
*/
492493
zMime = P("mimetype");
493494
--- src/doc.c
+++ src/doc.c
@@ -483,10 +483,11 @@
483 if( content_get(rid, &filebody)==0 ){
484 goto doc_not_found;
485 }
486 db_end_transaction(0);
487 }
 
488
489 /* The file is now contained in the filebody blob. Deliver the
490 ** file to the user
491 */
492 zMime = P("mimetype");
493
--- src/doc.c
+++ src/doc.c
@@ -483,10 +483,11 @@
483 if( content_get(rid, &filebody)==0 ){
484 goto doc_not_found;
485 }
486 db_end_transaction(0);
487 }
488 blob_to_utf8_no_bom(&filebody, 0);
489
490 /* The file is now contained in the filebody blob. Deliver the
491 ** file to the user
492 */
493 zMime = P("mimetype");
494
+31 -24
--- src/file.c
+++ src/file.c
@@ -703,11 +703,11 @@
703703
}
704704
}
705705
if( j>=0 ) z[j] = z[i];
706706
j++;
707707
}
708
- if( j==0 ) z[j++] = '.';
708
+ if( j==0 ) z[j++] = '/';
709709
z[j] = 0;
710710
return j;
711711
}
712712
713713
/*
@@ -777,39 +777,40 @@
777777
** Convert /A/../ to just /
778778
** If the slash parameter is non-zero, the trailing slash, if any,
779779
** is retained.
780780
*/
781781
void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){
782
+ blob_zero(pOut);
782783
if( file_is_absolute_path(zOrigName) ){
783
-#if defined(_WIN32) || defined(__CYGWIN__)
784
- char *zOut;
785
-#endif
786
- blob_set(pOut, zOrigName);
787
- blob_materialize(pOut);
784
+ blob_appendf(pOut, "%/", zOrigName);
785
+ }else{
786
+ char zPwd[2000];
787
+ file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName));
788
+ if( zPwd[0]=='/' && strlen(zPwd)==1 ){
789
+ /* when on '/', don't add an extra '/' */
790
+ if( zOrigName[0]=='.' && strlen(zOrigName)==1 ){
791
+ /* '.' when on '/' mean '/' */
792
+ blob_appendf(pOut, "%/", zPwd);
793
+ }else{
794
+ blob_appendf(pOut, "%/%/", zPwd, zOrigName);
795
+ }
796
+ }else{
797
+ blob_appendf(pOut, "%//%/", zPwd, zOrigName);
798
+ }
799
+ }
788800
#if defined(_WIN32) || defined(__CYGWIN__)
801
+ {
802
+ char *zOut;
789803
/*
790804
** On Windows/cygwin, normalize the drive letter to upper case.
791805
*/
792806
zOut = blob_str(pOut);
793
- if( fossil_islower(zOut[0]) && zOut[1]==':' ){
807
+ if( fossil_islower(zOut[0]) && zOut[1]==':' && zOut[2]=='/' ){
794808
zOut[0] = fossil_toupper(zOut[0]);
795809
}
796
-#endif
797
- }else{
798
- char zPwd[2000];
799
- file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName));
800
-#if defined(_WIN32)
801
- /*
802
- ** On Windows, normalize the drive letter to upper case.
803
- */
804
- if( fossil_islower(zPwd[0]) && zPwd[1]==':' ){
805
- zPwd[0] = fossil_toupper(zPwd[0]);
806
- }
807
-#endif
808
- blob_zero(pOut);
809
- blob_appendf(pOut, "%//%/", zPwd, zOrigName);
810
- }
810
+ }
811
+#endif
811812
blob_resize(pOut, file_simplify_name(blob_buffer(pOut),
812813
blob_size(pOut), slash));
813814
}
814815
815816
/*
@@ -928,11 +929,16 @@
928929
blob_append(pOut, &zPath[i+1], -1);
929930
blob_reset(&tmp);
930931
return;
931932
}
932933
while( zPath[i-1]!='/' ){ i--; }
933
- blob_set(&tmp, "../");
934
+ if( zPwd[0]=='/' && strlen(zPwd)==1 ){
935
+ /* If on '/', don't go to higher level */
936
+ blob_zero(&tmp);
937
+ }else{
938
+ blob_set(&tmp, "../");
939
+ }
934940
for(j=i; zPwd[j]; j++){
935941
if( zPwd[j]=='/' ){
936942
blob_append(&tmp, "../", 3);
937943
}
938944
}
@@ -990,11 +996,12 @@
990996
}else{
991997
xCmp = fossil_strnicmp;
992998
}
993999
9941000
/* Special case. zOrigName refers to g.zLocalRoot directory. */
995
- if( nFull==nLocalRoot-1 && xCmp(zLocalRoot, zFull, nFull)==0 ){
1001
+ if( (nFull==nLocalRoot-1 && xCmp(zLocalRoot, zFull, nFull)==0)
1002
+ || (nFull==1 && zFull[0]=='/' && nLocalRoot==1 && zLocalRoot[0]=='/') ){
9961003
blob_append(pOut, ".", 1);
9971004
blob_reset(&localRoot);
9981005
blob_reset(&full);
9991006
return 1;
10001007
}
10011008
--- src/file.c
+++ src/file.c
@@ -703,11 +703,11 @@
703 }
704 }
705 if( j>=0 ) z[j] = z[i];
706 j++;
707 }
708 if( j==0 ) z[j++] = '.';
709 z[j] = 0;
710 return j;
711 }
712
713 /*
@@ -777,39 +777,40 @@
777 ** Convert /A/../ to just /
778 ** If the slash parameter is non-zero, the trailing slash, if any,
779 ** is retained.
780 */
781 void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){
 
782 if( file_is_absolute_path(zOrigName) ){
783 #if defined(_WIN32) || defined(__CYGWIN__)
784 char *zOut;
785 #endif
786 blob_set(pOut, zOrigName);
787 blob_materialize(pOut);
 
 
 
 
 
 
 
 
 
 
 
788 #if defined(_WIN32) || defined(__CYGWIN__)
 
 
789 /*
790 ** On Windows/cygwin, normalize the drive letter to upper case.
791 */
792 zOut = blob_str(pOut);
793 if( fossil_islower(zOut[0]) && zOut[1]==':' ){
794 zOut[0] = fossil_toupper(zOut[0]);
795 }
796 #endif
797 }else{
798 char zPwd[2000];
799 file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName));
800 #if defined(_WIN32)
801 /*
802 ** On Windows, normalize the drive letter to upper case.
803 */
804 if( fossil_islower(zPwd[0]) && zPwd[1]==':' ){
805 zPwd[0] = fossil_toupper(zPwd[0]);
806 }
807 #endif
808 blob_zero(pOut);
809 blob_appendf(pOut, "%//%/", zPwd, zOrigName);
810 }
811 blob_resize(pOut, file_simplify_name(blob_buffer(pOut),
812 blob_size(pOut), slash));
813 }
814
815 /*
@@ -928,11 +929,16 @@
928 blob_append(pOut, &zPath[i+1], -1);
929 blob_reset(&tmp);
930 return;
931 }
932 while( zPath[i-1]!='/' ){ i--; }
933 blob_set(&tmp, "../");
 
 
 
 
 
934 for(j=i; zPwd[j]; j++){
935 if( zPwd[j]=='/' ){
936 blob_append(&tmp, "../", 3);
937 }
938 }
@@ -990,11 +996,12 @@
990 }else{
991 xCmp = fossil_strnicmp;
992 }
993
994 /* Special case. zOrigName refers to g.zLocalRoot directory. */
995 if( nFull==nLocalRoot-1 && xCmp(zLocalRoot, zFull, nFull)==0 ){
 
996 blob_append(pOut, ".", 1);
997 blob_reset(&localRoot);
998 blob_reset(&full);
999 return 1;
1000 }
1001
--- src/file.c
+++ src/file.c
@@ -703,11 +703,11 @@
703 }
704 }
705 if( j>=0 ) z[j] = z[i];
706 j++;
707 }
708 if( j==0 ) z[j++] = '/';
709 z[j] = 0;
710 return j;
711 }
712
713 /*
@@ -777,39 +777,40 @@
777 ** Convert /A/../ to just /
778 ** If the slash parameter is non-zero, the trailing slash, if any,
779 ** is retained.
780 */
781 void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){
782 blob_zero(pOut);
783 if( file_is_absolute_path(zOrigName) ){
784 blob_appendf(pOut, "%/", zOrigName);
785 }else{
786 char zPwd[2000];
787 file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName));
788 if( zPwd[0]=='/' && strlen(zPwd)==1 ){
789 /* when on '/', don't add an extra '/' */
790 if( zOrigName[0]=='.' && strlen(zOrigName)==1 ){
791 /* '.' when on '/' mean '/' */
792 blob_appendf(pOut, "%/", zPwd);
793 }else{
794 blob_appendf(pOut, "%/%/", zPwd, zOrigName);
795 }
796 }else{
797 blob_appendf(pOut, "%//%/", zPwd, zOrigName);
798 }
799 }
800 #if defined(_WIN32) || defined(__CYGWIN__)
801 {
802 char *zOut;
803 /*
804 ** On Windows/cygwin, normalize the drive letter to upper case.
805 */
806 zOut = blob_str(pOut);
807 if( fossil_islower(zOut[0]) && zOut[1]==':' && zOut[2]=='/' ){
808 zOut[0] = fossil_toupper(zOut[0]);
809 }
810 }
811 #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
812 blob_resize(pOut, file_simplify_name(blob_buffer(pOut),
813 blob_size(pOut), slash));
814 }
815
816 /*
@@ -928,11 +929,16 @@
929 blob_append(pOut, &zPath[i+1], -1);
930 blob_reset(&tmp);
931 return;
932 }
933 while( zPath[i-1]!='/' ){ i--; }
934 if( zPwd[0]=='/' && strlen(zPwd)==1 ){
935 /* If on '/', don't go to higher level */
936 blob_zero(&tmp);
937 }else{
938 blob_set(&tmp, "../");
939 }
940 for(j=i; zPwd[j]; j++){
941 if( zPwd[j]=='/' ){
942 blob_append(&tmp, "../", 3);
943 }
944 }
@@ -990,11 +996,12 @@
996 }else{
997 xCmp = fossil_strnicmp;
998 }
999
1000 /* Special case. zOrigName refers to g.zLocalRoot directory. */
1001 if( (nFull==nLocalRoot-1 && xCmp(zLocalRoot, zFull, nFull)==0)
1002 || (nFull==1 && zFull[0]=='/' && nLocalRoot==1 && zLocalRoot[0]=='/') ){
1003 blob_append(pOut, ".", 1);
1004 blob_reset(&localRoot);
1005 blob_reset(&full);
1006 return 1;
1007 }
1008
+7 -6
--- src/finfo.c
+++ src/finfo.c
@@ -164,11 +164,11 @@
164164
if( rid==0 ){
165165
fossil_fatal("no history for file: %b", &fname);
166166
}
167167
zFilename = blob_str(&fname);
168168
db_prepare(&q,
169
- "SELECT b.uuid, ci.uuid, date(event.mtime,'localtime'),"
169
+ "SELECT b.uuid, ci.uuid, date(event.mtime%s),"
170170
" coalesce(event.ecomment, event.comment),"
171171
" coalesce(event.euser, event.user),"
172172
" (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
173173
" AND tagxref.rid=mlink.mid)" /* Tags */
174174
" FROM mlink, blob b, event, blob ci, filename"
@@ -176,11 +176,12 @@
176176
" AND mlink.fnid=filename.fnid"
177177
" AND b.rid=mlink.fid"
178178
" AND event.objid=mlink.mid"
179179
" AND event.objid=ci.rid"
180180
" ORDER BY event.mtime DESC LIMIT %d OFFSET %d",
181
- TAG_BRANCH, zFilename, filename_collation(), iLimit, iOffset
181
+ timeline_utc(), TAG_BRANCH, zFilename, filename_collation(),
182
+ iLimit, iOffset
182183
);
183184
blob_zero(&line);
184185
if( iBrief ){
185186
fossil_print("History of %s\n", blob_str(&fname));
186187
}
@@ -302,11 +303,11 @@
302303
zFilename = PD("name","");
303304
url_add_parameter(&url, "name", zFilename);
304305
blob_zero(&sql);
305306
blob_appendf(&sql,
306307
"SELECT"
307
- " datetime(event.mtime,'localtime')," /* Date of change */
308
+ " datetime(event.mtime%s)," /* Date of change */
308309
" coalesce(event.ecomment, event.comment)," /* Check-in comment */
309310
" coalesce(event.euser, event.user)," /* User who made chng */
310311
" mlink.pid," /* Parent file rid */
311312
" mlink.fid," /* File rid */
312313
" (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */
@@ -315,11 +316,11 @@
315316
" event.bgcolor," /* Background color */
316317
" (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
317318
" AND tagxref.rid=mlink.mid)," /* Tags */
318319
" mlink.mid," /* check-in ID */
319320
" mlink.pfnid", /* Previous filename */
320
- TAG_BRANCH
321
+ timeline_utc(), TAG_BRANCH
321322
);
322323
if( firstChngOnly ){
323324
#if 0
324325
blob_appendf(&sql, ", min(event.mtime)");
325326
#else
@@ -375,16 +376,16 @@
375376
blob_zero(&title);
376377
if( baseCheckin ){
377378
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", baseCheckin);
378379
char *zLink = href("%R/info/%S", zUuid);
379380
blob_appendf(&title, "Ancestors of file ");
380
- hyperlinked_path(zFilename, &title, zUuid);
381
+ hyperlinked_path(zFilename, &title, zUuid, "tree", "");
381382
blob_appendf(&title, " from check-in %z%.10s</a>", zLink, zUuid);
382383
fossil_free(zUuid);
383384
}else{
384385
blob_appendf(&title, "History of files named ");
385
- hyperlinked_path(zFilename, &title, 0);
386
+ hyperlinked_path(zFilename, &title, 0, "tree", "");
386387
}
387388
@ <h2>%b(&title)</h2>
388389
blob_reset(&title);
389390
pGraph = graph_init();
390391
@ <div id="canvas" style="position:relative;width:1px;height:1px;"
391392
--- src/finfo.c
+++ src/finfo.c
@@ -164,11 +164,11 @@
164 if( rid==0 ){
165 fossil_fatal("no history for file: %b", &fname);
166 }
167 zFilename = blob_str(&fname);
168 db_prepare(&q,
169 "SELECT b.uuid, ci.uuid, date(event.mtime,'localtime'),"
170 " coalesce(event.ecomment, event.comment),"
171 " coalesce(event.euser, event.user),"
172 " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
173 " AND tagxref.rid=mlink.mid)" /* Tags */
174 " FROM mlink, blob b, event, blob ci, filename"
@@ -176,11 +176,12 @@
176 " AND mlink.fnid=filename.fnid"
177 " AND b.rid=mlink.fid"
178 " AND event.objid=mlink.mid"
179 " AND event.objid=ci.rid"
180 " ORDER BY event.mtime DESC LIMIT %d OFFSET %d",
181 TAG_BRANCH, zFilename, filename_collation(), iLimit, iOffset
 
182 );
183 blob_zero(&line);
184 if( iBrief ){
185 fossil_print("History of %s\n", blob_str(&fname));
186 }
@@ -302,11 +303,11 @@
302 zFilename = PD("name","");
303 url_add_parameter(&url, "name", zFilename);
304 blob_zero(&sql);
305 blob_appendf(&sql,
306 "SELECT"
307 " datetime(event.mtime,'localtime')," /* Date of change */
308 " coalesce(event.ecomment, event.comment)," /* Check-in comment */
309 " coalesce(event.euser, event.user)," /* User who made chng */
310 " mlink.pid," /* Parent file rid */
311 " mlink.fid," /* File rid */
312 " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */
@@ -315,11 +316,11 @@
315 " event.bgcolor," /* Background color */
316 " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
317 " AND tagxref.rid=mlink.mid)," /* Tags */
318 " mlink.mid," /* check-in ID */
319 " mlink.pfnid", /* Previous filename */
320 TAG_BRANCH
321 );
322 if( firstChngOnly ){
323 #if 0
324 blob_appendf(&sql, ", min(event.mtime)");
325 #else
@@ -375,16 +376,16 @@
375 blob_zero(&title);
376 if( baseCheckin ){
377 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", baseCheckin);
378 char *zLink = href("%R/info/%S", zUuid);
379 blob_appendf(&title, "Ancestors of file ");
380 hyperlinked_path(zFilename, &title, zUuid);
381 blob_appendf(&title, " from check-in %z%.10s</a>", zLink, zUuid);
382 fossil_free(zUuid);
383 }else{
384 blob_appendf(&title, "History of files named ");
385 hyperlinked_path(zFilename, &title, 0);
386 }
387 @ <h2>%b(&title)</h2>
388 blob_reset(&title);
389 pGraph = graph_init();
390 @ <div id="canvas" style="position:relative;width:1px;height:1px;"
391
--- src/finfo.c
+++ src/finfo.c
@@ -164,11 +164,11 @@
164 if( rid==0 ){
165 fossil_fatal("no history for file: %b", &fname);
166 }
167 zFilename = blob_str(&fname);
168 db_prepare(&q,
169 "SELECT b.uuid, ci.uuid, date(event.mtime%s),"
170 " coalesce(event.ecomment, event.comment),"
171 " coalesce(event.euser, event.user),"
172 " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
173 " AND tagxref.rid=mlink.mid)" /* Tags */
174 " FROM mlink, blob b, event, blob ci, filename"
@@ -176,11 +176,12 @@
176 " AND mlink.fnid=filename.fnid"
177 " AND b.rid=mlink.fid"
178 " AND event.objid=mlink.mid"
179 " AND event.objid=ci.rid"
180 " ORDER BY event.mtime DESC LIMIT %d OFFSET %d",
181 timeline_utc(), TAG_BRANCH, zFilename, filename_collation(),
182 iLimit, iOffset
183 );
184 blob_zero(&line);
185 if( iBrief ){
186 fossil_print("History of %s\n", blob_str(&fname));
187 }
@@ -302,11 +303,11 @@
303 zFilename = PD("name","");
304 url_add_parameter(&url, "name", zFilename);
305 blob_zero(&sql);
306 blob_appendf(&sql,
307 "SELECT"
308 " datetime(event.mtime%s)," /* Date of change */
309 " coalesce(event.ecomment, event.comment)," /* Check-in comment */
310 " coalesce(event.euser, event.user)," /* User who made chng */
311 " mlink.pid," /* Parent file rid */
312 " mlink.fid," /* File rid */
313 " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */
@@ -315,11 +316,11 @@
316 " event.bgcolor," /* Background color */
317 " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
318 " AND tagxref.rid=mlink.mid)," /* Tags */
319 " mlink.mid," /* check-in ID */
320 " mlink.pfnid", /* Previous filename */
321 timeline_utc(), TAG_BRANCH
322 );
323 if( firstChngOnly ){
324 #if 0
325 blob_appendf(&sql, ", min(event.mtime)");
326 #else
@@ -375,16 +376,16 @@
376 blob_zero(&title);
377 if( baseCheckin ){
378 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", baseCheckin);
379 char *zLink = href("%R/info/%S", zUuid);
380 blob_appendf(&title, "Ancestors of file ");
381 hyperlinked_path(zFilename, &title, zUuid, "tree", "");
382 blob_appendf(&title, " from check-in %z%.10s</a>", zLink, zUuid);
383 fossil_free(zUuid);
384 }else{
385 blob_appendf(&title, "History of files named ");
386 hyperlinked_path(zFilename, &title, 0, "tree", "");
387 }
388 @ <h2>%b(&title)</h2>
389 blob_reset(&title);
390 pGraph = graph_init();
391 @ <div id="canvas" style="position:relative;width:1px;height:1px;"
392
+15
--- src/import.c
+++ src/import.c
@@ -575,22 +575,37 @@
575575
fossil_free(gg.zMark);
576576
gg.zMark = fossil_strdup(&zLine[5]);
577577
}else
578578
if( memcmp(zLine, "tagger ", 7)==0 || memcmp(zLine, "committer ",10)==0 ){
579579
sqlite3_int64 secSince1970;
580
+ int hastz;
581
+ char tzdir;
582
+ int tz;
580583
for(i=0; zLine[i] && zLine[i]!='<'; i++){}
581584
if( zLine[i]==0 ) goto malformed_line;
582585
z = &zLine[i+1];
583586
for(i=i+1; zLine[i] && zLine[i]!='>'; i++){}
584587
if( zLine[i]==0 ) goto malformed_line;
585588
zLine[i] = 0;
586589
fossil_free(gg.zUser);
587590
gg.zUser = fossil_strdup(z);
588591
secSince1970 = 0;
592
+
593
+ /* We don't use sscanf here because of int64 portability issues. */
589594
for(i=i+2; fossil_isdigit(zLine[i]); i++){
590595
secSince1970 = secSince1970*10 + zLine[i] - '0';
591596
}
597
+
598
+ /* Read in optional timezone modifier (we don't know if it's strictly
599
+ * optional, but better to be sure). */
600
+ tzdir = '+';
601
+ tz = 0;
602
+ hastz = sscanf(&zLine[i], " %c%d", &tzdir, &tz);
603
+ if ((hastz == 1) || (hastz > 2)) goto malformed_line;
604
+ secSince1970 += ((tzdir == '-') ? -1 : 1) *
605
+ ((tz/100)*3600 + (tz%100)*60);
606
+
592607
fossil_free(gg.zDate);
593608
gg.zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", secSince1970);
594609
gg.zDate[10] = 'T';
595610
}else
596611
if( memcmp(zLine, "from ", 5)==0 ){
597612
--- src/import.c
+++ src/import.c
@@ -575,22 +575,37 @@
575 fossil_free(gg.zMark);
576 gg.zMark = fossil_strdup(&zLine[5]);
577 }else
578 if( memcmp(zLine, "tagger ", 7)==0 || memcmp(zLine, "committer ",10)==0 ){
579 sqlite3_int64 secSince1970;
 
 
 
580 for(i=0; zLine[i] && zLine[i]!='<'; i++){}
581 if( zLine[i]==0 ) goto malformed_line;
582 z = &zLine[i+1];
583 for(i=i+1; zLine[i] && zLine[i]!='>'; i++){}
584 if( zLine[i]==0 ) goto malformed_line;
585 zLine[i] = 0;
586 fossil_free(gg.zUser);
587 gg.zUser = fossil_strdup(z);
588 secSince1970 = 0;
 
 
589 for(i=i+2; fossil_isdigit(zLine[i]); i++){
590 secSince1970 = secSince1970*10 + zLine[i] - '0';
591 }
 
 
 
 
 
 
 
 
 
 
592 fossil_free(gg.zDate);
593 gg.zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", secSince1970);
594 gg.zDate[10] = 'T';
595 }else
596 if( memcmp(zLine, "from ", 5)==0 ){
597
--- src/import.c
+++ src/import.c
@@ -575,22 +575,37 @@
575 fossil_free(gg.zMark);
576 gg.zMark = fossil_strdup(&zLine[5]);
577 }else
578 if( memcmp(zLine, "tagger ", 7)==0 || memcmp(zLine, "committer ",10)==0 ){
579 sqlite3_int64 secSince1970;
580 int hastz;
581 char tzdir;
582 int tz;
583 for(i=0; zLine[i] && zLine[i]!='<'; i++){}
584 if( zLine[i]==0 ) goto malformed_line;
585 z = &zLine[i+1];
586 for(i=i+1; zLine[i] && zLine[i]!='>'; i++){}
587 if( zLine[i]==0 ) goto malformed_line;
588 zLine[i] = 0;
589 fossil_free(gg.zUser);
590 gg.zUser = fossil_strdup(z);
591 secSince1970 = 0;
592
593 /* We don't use sscanf here because of int64 portability issues. */
594 for(i=i+2; fossil_isdigit(zLine[i]); i++){
595 secSince1970 = secSince1970*10 + zLine[i] - '0';
596 }
597
598 /* Read in optional timezone modifier (we don't know if it's strictly
599 * optional, but better to be sure). */
600 tzdir = '+';
601 tz = 0;
602 hastz = sscanf(&zLine[i], " %c%d", &tzdir, &tz);
603 if ((hastz == 1) || (hastz > 2)) goto malformed_line;
604 secSince1970 += ((tzdir == '-') ? -1 : 1) *
605 ((tz/100)*3600 + (tz%100)*60);
606
607 fossil_free(gg.zDate);
608 gg.zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", secSince1970);
609 gg.zDate[10] = 'T';
610 }else
611 if( memcmp(zLine, "from ", 5)==0 ){
612
+15 -8
--- src/info.c
+++ src/info.c
@@ -230,15 +230,15 @@
230230
Stmt q;
231231
int cnt = 0;
232232
db_prepare(&q,
233233
"SELECT tag.tagid, tagname, "
234234
" (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
235
- " value, datetime(tagxref.mtime,'localtime'), tagtype,"
235
+ " value, datetime(tagxref.mtime%s), tagtype,"
236236
" (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
237237
" FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
238238
" WHERE tagxref.rid=%d AND tagname NOT GLOB '%q'"
239
- " ORDER BY tagname /*sort*/", rid, rid, rid, zNotGlob
239
+ " ORDER BY tagname /*sort*/", rid, timeline_utc(), rid, rid, zNotGlob
240240
);
241241
while( db_step(&q)==SQLITE_ROW ){
242242
const char *zTagname = db_column_text(&q, 1);
243243
const char *zSrcUuid = db_column_text(&q, 2);
244244
const char *zValue = db_column_text(&q, 3);
@@ -503,16 +503,16 @@
503503
" WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
504504
rid
505505
);
506506
isLeaf = is_a_leaf(rid);
507507
db_prepare(&q1,
508
- "SELECT uuid, datetime(mtime, 'localtime'), user, comment,"
509
- " datetime(omtime, 'localtime'), mtime"
508
+ "SELECT uuid, datetime(mtime%s), user, comment,"
509
+ " datetime(omtime%s), mtime"
510510
" FROM blob, event"
511511
" WHERE blob.rid=%d"
512512
" AND event.objid=%d",
513
- rid, rid
513
+ timeline_utc(), timeline_utc(), rid, rid
514514
);
515515
sideBySide = !is_false(PD("sbs","1"));
516516
if( db_step(&q1)==SQLITE_ROW ){
517517
const char *zUuid = db_column_text(&q1, 0);
518518
char *zTitle = mprintf("Check-in [%.10s]", zUuid);
@@ -610,11 +610,11 @@
610610
" WHERE rid=%d AND tagtype>0 "
611611
" AND tag.tagid=tagxref.tagid "
612612
" AND +tag.tagname GLOB 'sym-*'", rid);
613613
while( db_step(&q2)==SQLITE_ROW ){
614614
const char *zTagName = db_column_text(&q2, 0);
615
- @ | %z(href("%R/timeline?r=%T",zTagName))%h(zTagName)</a>
615
+ @ | %z(href("%R/timeline?r=%T&unhide",zTagName))%h(zTagName)</a>
616616
}
617617
db_finalize(&q2);
618618
619619
620620
/* The Download: line */
@@ -629,12 +629,13 @@
629629
fossil_free(zUrl);
630630
}
631631
@ </td></tr>
632632
@ <tr><th>Other&nbsp;Links:</th>
633633
@ <td>
634
- @ %z(href("%R/dir?ci=%S",zUuid))files</a>
634
+ @ %z(href("%R/tree?ci=%S",zUuid))files</a>
635635
@ | %z(href("%R/fileage?name=%S",zUuid))file ages</a>
636
+ @ | %z(href("%R/tree?ci=%S&nofiles",zUuid))folders</a>
636637
@ | %z(href("%R/artifact/%S",zUuid))manifest</a>
637638
if( g.perm.Write ){
638639
@ | %z(href("%R/ci_edit?r=%S",zUuid))edit</a>
639640
}
640641
@ </td>
@@ -1367,17 +1368,23 @@
13671368
** Return the uninterpreted content of an artifact. Used primarily
13681369
** to view artifacts that are images.
13691370
*/
13701371
void rawartifact_page(void){
13711372
int rid;
1373
+ char *zUuid;
13721374
const char *zMime;
13731375
Blob content;
13741376
13751377
rid = name_to_rid_www("name");
13761378
login_check_credentials();
13771379
if( !g.perm.Read ){ login_needed(); return; }
13781380
if( rid==0 ) fossil_redirect_home();
1381
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
1382
+ if( fossil_strcmp(P("name"), zUuid)==0 ){
1383
+ g.isConst = 1;
1384
+ }
1385
+ free(zUuid);
13791386
zMime = P("m");
13801387
if( zMime==0 ){
13811388
char *zFName = db_text(0, "SELECT filename.name FROM mlink, filename"
13821389
" WHERE mlink.fid=%d"
13831390
" AND filename.fnid=mlink.fnid", rid);
@@ -1674,16 +1681,16 @@
16741681
@ sandbox="allow-same-origin"
16751682
@ onload="this.height = this.contentDocument.documentElement.scrollHeight;">
16761683
@ </iframe>
16771684
}else{
16781685
style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
1686
+ blob_to_utf8_no_bom(&content, 0);
16791687
zMime = mimetype_from_content(&content);
16801688
@ <blockquote>
16811689
if( zMime==0 ){
16821690
const char *zLn = P("ln");
16831691
const char *z;
1684
- blob_to_utf8_no_bom(&content, 0);
16851692
z = blob_str(&content);
16861693
if( zLn ){
16871694
output_text_with_line_numbers(z, zLn);
16881695
}else{
16891696
@ <pre>
16901697
--- src/info.c
+++ src/info.c
@@ -230,15 +230,15 @@
230 Stmt q;
231 int cnt = 0;
232 db_prepare(&q,
233 "SELECT tag.tagid, tagname, "
234 " (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
235 " value, datetime(tagxref.mtime,'localtime'), tagtype,"
236 " (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
237 " FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
238 " WHERE tagxref.rid=%d AND tagname NOT GLOB '%q'"
239 " ORDER BY tagname /*sort*/", rid, rid, rid, zNotGlob
240 );
241 while( db_step(&q)==SQLITE_ROW ){
242 const char *zTagname = db_column_text(&q, 1);
243 const char *zSrcUuid = db_column_text(&q, 2);
244 const char *zValue = db_column_text(&q, 3);
@@ -503,16 +503,16 @@
503 " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
504 rid
505 );
506 isLeaf = is_a_leaf(rid);
507 db_prepare(&q1,
508 "SELECT uuid, datetime(mtime, 'localtime'), user, comment,"
509 " datetime(omtime, 'localtime'), mtime"
510 " FROM blob, event"
511 " WHERE blob.rid=%d"
512 " AND event.objid=%d",
513 rid, rid
514 );
515 sideBySide = !is_false(PD("sbs","1"));
516 if( db_step(&q1)==SQLITE_ROW ){
517 const char *zUuid = db_column_text(&q1, 0);
518 char *zTitle = mprintf("Check-in [%.10s]", zUuid);
@@ -610,11 +610,11 @@
610 " WHERE rid=%d AND tagtype>0 "
611 " AND tag.tagid=tagxref.tagid "
612 " AND +tag.tagname GLOB 'sym-*'", rid);
613 while( db_step(&q2)==SQLITE_ROW ){
614 const char *zTagName = db_column_text(&q2, 0);
615 @ | %z(href("%R/timeline?r=%T",zTagName))%h(zTagName)</a>
616 }
617 db_finalize(&q2);
618
619
620 /* The Download: line */
@@ -629,12 +629,13 @@
629 fossil_free(zUrl);
630 }
631 @ </td></tr>
632 @ <tr><th>Other&nbsp;Links:</th>
633 @ <td>
634 @ %z(href("%R/dir?ci=%S",zUuid))files</a>
635 @ | %z(href("%R/fileage?name=%S",zUuid))file ages</a>
 
636 @ | %z(href("%R/artifact/%S",zUuid))manifest</a>
637 if( g.perm.Write ){
638 @ | %z(href("%R/ci_edit?r=%S",zUuid))edit</a>
639 }
640 @ </td>
@@ -1367,17 +1368,23 @@
1367 ** Return the uninterpreted content of an artifact. Used primarily
1368 ** to view artifacts that are images.
1369 */
1370 void rawartifact_page(void){
1371 int rid;
 
1372 const char *zMime;
1373 Blob content;
1374
1375 rid = name_to_rid_www("name");
1376 login_check_credentials();
1377 if( !g.perm.Read ){ login_needed(); return; }
1378 if( rid==0 ) fossil_redirect_home();
 
 
 
 
 
1379 zMime = P("m");
1380 if( zMime==0 ){
1381 char *zFName = db_text(0, "SELECT filename.name FROM mlink, filename"
1382 " WHERE mlink.fid=%d"
1383 " AND filename.fnid=mlink.fnid", rid);
@@ -1674,16 +1681,16 @@
1674 @ sandbox="allow-same-origin"
1675 @ onload="this.height = this.contentDocument.documentElement.scrollHeight;">
1676 @ </iframe>
1677 }else{
1678 style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
 
1679 zMime = mimetype_from_content(&content);
1680 @ <blockquote>
1681 if( zMime==0 ){
1682 const char *zLn = P("ln");
1683 const char *z;
1684 blob_to_utf8_no_bom(&content, 0);
1685 z = blob_str(&content);
1686 if( zLn ){
1687 output_text_with_line_numbers(z, zLn);
1688 }else{
1689 @ <pre>
1690
--- src/info.c
+++ src/info.c
@@ -230,15 +230,15 @@
230 Stmt q;
231 int cnt = 0;
232 db_prepare(&q,
233 "SELECT tag.tagid, tagname, "
234 " (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
235 " value, datetime(tagxref.mtime%s), tagtype,"
236 " (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
237 " FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
238 " WHERE tagxref.rid=%d AND tagname NOT GLOB '%q'"
239 " ORDER BY tagname /*sort*/", rid, timeline_utc(), rid, rid, zNotGlob
240 );
241 while( db_step(&q)==SQLITE_ROW ){
242 const char *zTagname = db_column_text(&q, 1);
243 const char *zSrcUuid = db_column_text(&q, 2);
244 const char *zValue = db_column_text(&q, 3);
@@ -503,16 +503,16 @@
503 " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
504 rid
505 );
506 isLeaf = is_a_leaf(rid);
507 db_prepare(&q1,
508 "SELECT uuid, datetime(mtime%s), user, comment,"
509 " datetime(omtime%s), mtime"
510 " FROM blob, event"
511 " WHERE blob.rid=%d"
512 " AND event.objid=%d",
513 timeline_utc(), timeline_utc(), rid, rid
514 );
515 sideBySide = !is_false(PD("sbs","1"));
516 if( db_step(&q1)==SQLITE_ROW ){
517 const char *zUuid = db_column_text(&q1, 0);
518 char *zTitle = mprintf("Check-in [%.10s]", zUuid);
@@ -610,11 +610,11 @@
610 " WHERE rid=%d AND tagtype>0 "
611 " AND tag.tagid=tagxref.tagid "
612 " AND +tag.tagname GLOB 'sym-*'", rid);
613 while( db_step(&q2)==SQLITE_ROW ){
614 const char *zTagName = db_column_text(&q2, 0);
615 @ | %z(href("%R/timeline?r=%T&unhide",zTagName))%h(zTagName)</a>
616 }
617 db_finalize(&q2);
618
619
620 /* The Download: line */
@@ -629,12 +629,13 @@
629 fossil_free(zUrl);
630 }
631 @ </td></tr>
632 @ <tr><th>Other&nbsp;Links:</th>
633 @ <td>
634 @ %z(href("%R/tree?ci=%S",zUuid))files</a>
635 @ | %z(href("%R/fileage?name=%S",zUuid))file ages</a>
636 @ | %z(href("%R/tree?ci=%S&nofiles",zUuid))folders</a>
637 @ | %z(href("%R/artifact/%S",zUuid))manifest</a>
638 if( g.perm.Write ){
639 @ | %z(href("%R/ci_edit?r=%S",zUuid))edit</a>
640 }
641 @ </td>
@@ -1367,17 +1368,23 @@
1368 ** Return the uninterpreted content of an artifact. Used primarily
1369 ** to view artifacts that are images.
1370 */
1371 void rawartifact_page(void){
1372 int rid;
1373 char *zUuid;
1374 const char *zMime;
1375 Blob content;
1376
1377 rid = name_to_rid_www("name");
1378 login_check_credentials();
1379 if( !g.perm.Read ){ login_needed(); return; }
1380 if( rid==0 ) fossil_redirect_home();
1381 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
1382 if( fossil_strcmp(P("name"), zUuid)==0 ){
1383 g.isConst = 1;
1384 }
1385 free(zUuid);
1386 zMime = P("m");
1387 if( zMime==0 ){
1388 char *zFName = db_text(0, "SELECT filename.name FROM mlink, filename"
1389 " WHERE mlink.fid=%d"
1390 " AND filename.fnid=mlink.fnid", rid);
@@ -1674,16 +1681,16 @@
1681 @ sandbox="allow-same-origin"
1682 @ onload="this.height = this.contentDocument.documentElement.scrollHeight;">
1683 @ </iframe>
1684 }else{
1685 style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
1686 blob_to_utf8_no_bom(&content, 0);
1687 zMime = mimetype_from_content(&content);
1688 @ <blockquote>
1689 if( zMime==0 ){
1690 const char *zLn = P("ln");
1691 const char *z;
 
1692 z = blob_str(&content);
1693 if( zLn ){
1694 output_text_with_line_numbers(z, zLn);
1695 }else{
1696 @ <pre>
1697
+44
--- src/main.c
+++ src/main.c
@@ -377,10 +377,20 @@
377377
#endif
378378
free(g.zErrMsg);
379379
if(g.db){
380380
db_close(0);
381381
}
382
+ /*
383
+ ** FIXME: The next two lines cannot always be enabled; however, they
384
+ ** are very useful for tracking down TH1 memory leaks.
385
+ */
386
+ if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
387
+ if( g.interp ){
388
+ Th_DeleteInterp(g.interp); g.interp = 0;
389
+ }
390
+ assert( Th_GetOutstandingMalloc()==0 );
391
+ }
382392
}
383393
384394
/*
385395
** Convert all arguments from mbcs (or unicode) to UTF-8. Then
386396
** search g.argv for arguments "--args FILENAME". If found, then
@@ -559,10 +569,14 @@
559569
#endif
560570
{
561571
const char *zCmdName = "unknown";
562572
int idx;
563573
int rc;
574
+ if( sqlite3_libversion_number()<3008002 ){
575
+ fossil_fatal("Unsuitable SQLite version %s, must be at least 3.8.2",
576
+ sqlite3_libversion());
577
+ }
564578
sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
565579
sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
566580
memset(&g, 0, sizeof(g));
567581
g.now = time(0);
568582
g.httpHeader = empty_blob;
@@ -1072,10 +1086,40 @@
10721086
if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){
10731087
@ <li><a href="%s(g.zTop)/help?cmd=%s(z)">%s(z+1)</a></li>
10741088
}else{
10751089
@ <li>%s(z+1)</li>
10761090
}
1091
+ j++;
1092
+ if( j>=n ){
1093
+ @ </ul></td>
1094
+ j = 0;
1095
+ }
1096
+ }
1097
+ if( j>0 ){
1098
+ @ </ul></td>
1099
+ }
1100
+ @ </tr></table>
1101
+
1102
+ @ <h1>Unsupported commands:</h1>
1103
+ @ <table border="0"><tr>
1104
+ for(i=j=0; i<count(aCommand); i++){
1105
+ const char *z = aCommand[i].zName;
1106
+ if( strncmp(z,"test",4)!=0 ) continue;
1107
+ j++;
1108
+ }
1109
+ n = (j+3)/4;
1110
+ for(i=j=0; i<count(aCommand); i++){
1111
+ const char *z = aCommand[i].zName;
1112
+ if( strncmp(z,"test",4)!=0 ) continue;
1113
+ if( j==0 ){
1114
+ @ <td valign="top"><ul>
1115
+ }
1116
+ if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){
1117
+ @ <li><a href="%s(g.zTop)/help?cmd=%s(z)">%s(z)</a></li>
1118
+ }else{
1119
+ @ <li>%s(z)</li>
1120
+ }
10771121
j++;
10781122
if( j>=n ){
10791123
@ </ul></td>
10801124
j = 0;
10811125
}
10821126
--- src/main.c
+++ src/main.c
@@ -377,10 +377,20 @@
377 #endif
378 free(g.zErrMsg);
379 if(g.db){
380 db_close(0);
381 }
 
 
 
 
 
 
 
 
 
 
382 }
383
384 /*
385 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
386 ** search g.argv for arguments "--args FILENAME". If found, then
@@ -559,10 +569,14 @@
559 #endif
560 {
561 const char *zCmdName = "unknown";
562 int idx;
563 int rc;
 
 
 
 
564 sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
565 sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
566 memset(&g, 0, sizeof(g));
567 g.now = time(0);
568 g.httpHeader = empty_blob;
@@ -1072,10 +1086,40 @@
1072 if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){
1073 @ <li><a href="%s(g.zTop)/help?cmd=%s(z)">%s(z+1)</a></li>
1074 }else{
1075 @ <li>%s(z+1)</li>
1076 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1077 j++;
1078 if( j>=n ){
1079 @ </ul></td>
1080 j = 0;
1081 }
1082
--- src/main.c
+++ src/main.c
@@ -377,10 +377,20 @@
377 #endif
378 free(g.zErrMsg);
379 if(g.db){
380 db_close(0);
381 }
382 /*
383 ** FIXME: The next two lines cannot always be enabled; however, they
384 ** are very useful for tracking down TH1 memory leaks.
385 */
386 if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
387 if( g.interp ){
388 Th_DeleteInterp(g.interp); g.interp = 0;
389 }
390 assert( Th_GetOutstandingMalloc()==0 );
391 }
392 }
393
394 /*
395 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
396 ** search g.argv for arguments "--args FILENAME". If found, then
@@ -559,10 +569,14 @@
569 #endif
570 {
571 const char *zCmdName = "unknown";
572 int idx;
573 int rc;
574 if( sqlite3_libversion_number()<3008002 ){
575 fossil_fatal("Unsuitable SQLite version %s, must be at least 3.8.2",
576 sqlite3_libversion());
577 }
578 sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
579 sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
580 memset(&g, 0, sizeof(g));
581 g.now = time(0);
582 g.httpHeader = empty_blob;
@@ -1072,10 +1086,40 @@
1086 if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){
1087 @ <li><a href="%s(g.zTop)/help?cmd=%s(z)">%s(z+1)</a></li>
1088 }else{
1089 @ <li>%s(z+1)</li>
1090 }
1091 j++;
1092 if( j>=n ){
1093 @ </ul></td>
1094 j = 0;
1095 }
1096 }
1097 if( j>0 ){
1098 @ </ul></td>
1099 }
1100 @ </tr></table>
1101
1102 @ <h1>Unsupported commands:</h1>
1103 @ <table border="0"><tr>
1104 for(i=j=0; i<count(aCommand); i++){
1105 const char *z = aCommand[i].zName;
1106 if( strncmp(z,"test",4)!=0 ) continue;
1107 j++;
1108 }
1109 n = (j+3)/4;
1110 for(i=j=0; i<count(aCommand); i++){
1111 const char *z = aCommand[i].zName;
1112 if( strncmp(z,"test",4)!=0 ) continue;
1113 if( j==0 ){
1114 @ <td valign="top"><ul>
1115 }
1116 if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){
1117 @ <li><a href="%s(g.zTop)/help?cmd=%s(z)">%s(z)</a></li>
1118 }else{
1119 @ <li>%s(z)</li>
1120 }
1121 j++;
1122 if( j>=n ){
1123 @ </ul></td>
1124 j = 0;
1125 }
1126
+44
--- src/main.c
+++ src/main.c
@@ -377,10 +377,20 @@
377377
#endif
378378
free(g.zErrMsg);
379379
if(g.db){
380380
db_close(0);
381381
}
382
+ /*
383
+ ** FIXME: The next two lines cannot always be enabled; however, they
384
+ ** are very useful for tracking down TH1 memory leaks.
385
+ */
386
+ if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
387
+ if( g.interp ){
388
+ Th_DeleteInterp(g.interp); g.interp = 0;
389
+ }
390
+ assert( Th_GetOutstandingMalloc()==0 );
391
+ }
382392
}
383393
384394
/*
385395
** Convert all arguments from mbcs (or unicode) to UTF-8. Then
386396
** search g.argv for arguments "--args FILENAME". If found, then
@@ -559,10 +569,14 @@
559569
#endif
560570
{
561571
const char *zCmdName = "unknown";
562572
int idx;
563573
int rc;
574
+ if( sqlite3_libversion_number()<3008002 ){
575
+ fossil_fatal("Unsuitable SQLite version %s, must be at least 3.8.2",
576
+ sqlite3_libversion());
577
+ }
564578
sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
565579
sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
566580
memset(&g, 0, sizeof(g));
567581
g.now = time(0);
568582
g.httpHeader = empty_blob;
@@ -1072,10 +1086,40 @@
10721086
if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){
10731087
@ <li><a href="%s(g.zTop)/help?cmd=%s(z)">%s(z+1)</a></li>
10741088
}else{
10751089
@ <li>%s(z+1)</li>
10761090
}
1091
+ j++;
1092
+ if( j>=n ){
1093
+ @ </ul></td>
1094
+ j = 0;
1095
+ }
1096
+ }
1097
+ if( j>0 ){
1098
+ @ </ul></td>
1099
+ }
1100
+ @ </tr></table>
1101
+
1102
+ @ <h1>Unsupported commands:</h1>
1103
+ @ <table border="0"><tr>
1104
+ for(i=j=0; i<count(aCommand); i++){
1105
+ const char *z = aCommand[i].zName;
1106
+ if( strncmp(z,"test",4)!=0 ) continue;
1107
+ j++;
1108
+ }
1109
+ n = (j+3)/4;
1110
+ for(i=j=0; i<count(aCommand); i++){
1111
+ const char *z = aCommand[i].zName;
1112
+ if( strncmp(z,"test",4)!=0 ) continue;
1113
+ if( j==0 ){
1114
+ @ <td valign="top"><ul>
1115
+ }
1116
+ if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){
1117
+ @ <li><a href="%s(g.zTop)/help?cmd=%s(z)">%s(z)</a></li>
1118
+ }else{
1119
+ @ <li>%s(z)</li>
1120
+ }
10771121
j++;
10781122
if( j>=n ){
10791123
@ </ul></td>
10801124
j = 0;
10811125
}
10821126
--- src/main.c
+++ src/main.c
@@ -377,10 +377,20 @@
377 #endif
378 free(g.zErrMsg);
379 if(g.db){
380 db_close(0);
381 }
 
 
 
 
 
 
 
 
 
 
382 }
383
384 /*
385 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
386 ** search g.argv for arguments "--args FILENAME". If found, then
@@ -559,10 +569,14 @@
559 #endif
560 {
561 const char *zCmdName = "unknown";
562 int idx;
563 int rc;
 
 
 
 
564 sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
565 sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
566 memset(&g, 0, sizeof(g));
567 g.now = time(0);
568 g.httpHeader = empty_blob;
@@ -1072,10 +1086,40 @@
1072 if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){
1073 @ <li><a href="%s(g.zTop)/help?cmd=%s(z)">%s(z+1)</a></li>
1074 }else{
1075 @ <li>%s(z+1)</li>
1076 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1077 j++;
1078 if( j>=n ){
1079 @ </ul></td>
1080 j = 0;
1081 }
1082
--- src/main.c
+++ src/main.c
@@ -377,10 +377,20 @@
377 #endif
378 free(g.zErrMsg);
379 if(g.db){
380 db_close(0);
381 }
382 /*
383 ** FIXME: The next two lines cannot always be enabled; however, they
384 ** are very useful for tracking down TH1 memory leaks.
385 */
386 if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
387 if( g.interp ){
388 Th_DeleteInterp(g.interp); g.interp = 0;
389 }
390 assert( Th_GetOutstandingMalloc()==0 );
391 }
392 }
393
394 /*
395 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
396 ** search g.argv for arguments "--args FILENAME". If found, then
@@ -559,10 +569,14 @@
569 #endif
570 {
571 const char *zCmdName = "unknown";
572 int idx;
573 int rc;
574 if( sqlite3_libversion_number()<3008002 ){
575 fossil_fatal("Unsuitable SQLite version %s, must be at least 3.8.2",
576 sqlite3_libversion());
577 }
578 sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
579 sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
580 memset(&g, 0, sizeof(g));
581 g.now = time(0);
582 g.httpHeader = empty_blob;
@@ -1072,10 +1086,40 @@
1086 if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){
1087 @ <li><a href="%s(g.zTop)/help?cmd=%s(z)">%s(z+1)</a></li>
1088 }else{
1089 @ <li>%s(z+1)</li>
1090 }
1091 j++;
1092 if( j>=n ){
1093 @ </ul></td>
1094 j = 0;
1095 }
1096 }
1097 if( j>0 ){
1098 @ </ul></td>
1099 }
1100 @ </tr></table>
1101
1102 @ <h1>Unsupported commands:</h1>
1103 @ <table border="0"><tr>
1104 for(i=j=0; i<count(aCommand); i++){
1105 const char *z = aCommand[i].zName;
1106 if( strncmp(z,"test",4)!=0 ) continue;
1107 j++;
1108 }
1109 n = (j+3)/4;
1110 for(i=j=0; i<count(aCommand); i++){
1111 const char *z = aCommand[i].zName;
1112 if( strncmp(z,"test",4)!=0 ) continue;
1113 if( j==0 ){
1114 @ <td valign="top"><ul>
1115 }
1116 if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){
1117 @ <li><a href="%s(g.zTop)/help?cmd=%s(z)">%s(z)</a></li>
1118 }else{
1119 @ <li>%s(z)</li>
1120 }
1121 j++;
1122 if( j>=n ){
1123 @ </ul></td>
1124 j = 0;
1125 }
1126
+4 -6
--- src/main.mk
+++ src/main.mk
@@ -376,39 +376,37 @@
376376
377377
$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
378378
$(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
379379
380380
# Setup the options used to compile the included SQLite library.
381
-SQLITE_OPTIONS = -Dlocaltime=fossil_localtime \
382
- -DSQLITE_OMIT_LOAD_EXTENSION=1 \
381
+SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 \
383382
-DSQLITE_ENABLE_LOCKING_STYLE=0 \
384383
-DSQLITE_THREADSAFE=0 \
385384
-DSQLITE_DEFAULT_FILE_FORMAT=4 \
386385
-DSQLITE_OMIT_DEPRECATED \
387386
-DSQLITE_ENABLE_EXPLAIN_COMMENTS
388387
389388
# Setup the options used to compile the included SQLite shell.
390389
SHELL_OPTIONS = -Dmain=sqlite3_shell \
391
- -DSQLITE_OMIT_LOAD_EXTENSION=1 \
392
- -Dsqlite3_strglob=strglob
390
+ -DSQLITE_OMIT_LOAD_EXTENSION=1
393391
394392
# The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
395393
# to 1. If it is set to 1, then there is no need to build or link
396394
# the sqlite3.o object. Instead, the system sqlite will be linked
397395
# using -lsqlite3.
398396
SQLITE3_OBJ.1 =
399
-SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
397
+SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o
400398
SQLITE3_OBJ. = $(SQLITE3_OBJ.0)
401399
402400
# The FOSSIL_ENABLE_TCL variable may be undefined, set to 0, or set to 1.
403401
# If it is set to 1, then we need to build the Tcl integration code and
404402
# link to the Tcl library.
405403
TCL_OBJ.0 =
406404
TCL_OBJ.1 = $(OBJDIR)/th_tcl.o
407405
TCL_OBJ. = $(TCL_OBJ.0)
408406
409
-EXTRAOBJ = $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) $(OBJDIR)/cson_amalgamation.o
407
+EXTRAOBJ = $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) $(OBJDIR)/cson_amalgamation.o
410408
411409
$(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ)
412410
$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)
413411
414412
# This rule prevents make from using its default rules to try build
415413
--- src/main.mk
+++ src/main.mk
@@ -376,39 +376,37 @@
376
377 $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
378 $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
379
380 # Setup the options used to compile the included SQLite library.
381 SQLITE_OPTIONS = -Dlocaltime=fossil_localtime \
382 -DSQLITE_OMIT_LOAD_EXTENSION=1 \
383 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
384 -DSQLITE_THREADSAFE=0 \
385 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
386 -DSQLITE_OMIT_DEPRECATED \
387 -DSQLITE_ENABLE_EXPLAIN_COMMENTS
388
389 # Setup the options used to compile the included SQLite shell.
390 SHELL_OPTIONS = -Dmain=sqlite3_shell \
391 -DSQLITE_OMIT_LOAD_EXTENSION=1 \
392 -Dsqlite3_strglob=strglob
393
394 # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
395 # to 1. If it is set to 1, then there is no need to build or link
396 # the sqlite3.o object. Instead, the system sqlite will be linked
397 # using -lsqlite3.
398 SQLITE3_OBJ.1 =
399 SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
400 SQLITE3_OBJ. = $(SQLITE3_OBJ.0)
401
402 # The FOSSIL_ENABLE_TCL variable may be undefined, set to 0, or set to 1.
403 # If it is set to 1, then we need to build the Tcl integration code and
404 # link to the Tcl library.
405 TCL_OBJ.0 =
406 TCL_OBJ.1 = $(OBJDIR)/th_tcl.o
407 TCL_OBJ. = $(TCL_OBJ.0)
408
409 EXTRAOBJ = $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) $(OBJDIR)/cson_amalgamation.o
410
411 $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ)
412 $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)
413
414 # This rule prevents make from using its default rules to try build
415
--- src/main.mk
+++ src/main.mk
@@ -376,39 +376,37 @@
376
377 $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
378 $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
379
380 # Setup the options used to compile the included SQLite library.
381 SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 \
 
382 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
383 -DSQLITE_THREADSAFE=0 \
384 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
385 -DSQLITE_OMIT_DEPRECATED \
386 -DSQLITE_ENABLE_EXPLAIN_COMMENTS
387
388 # Setup the options used to compile the included SQLite shell.
389 SHELL_OPTIONS = -Dmain=sqlite3_shell \
390 -DSQLITE_OMIT_LOAD_EXTENSION=1
 
391
392 # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
393 # to 1. If it is set to 1, then there is no need to build or link
394 # the sqlite3.o object. Instead, the system sqlite will be linked
395 # using -lsqlite3.
396 SQLITE3_OBJ.1 =
397 SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o
398 SQLITE3_OBJ. = $(SQLITE3_OBJ.0)
399
400 # The FOSSIL_ENABLE_TCL variable may be undefined, set to 0, or set to 1.
401 # If it is set to 1, then we need to build the Tcl integration code and
402 # link to the Tcl library.
403 TCL_OBJ.0 =
404 TCL_OBJ.1 = $(OBJDIR)/th_tcl.o
405 TCL_OBJ. = $(TCL_OBJ.0)
406
407 EXTRAOBJ = $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) $(OBJDIR)/cson_amalgamation.o
408
409 $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ)
410 $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)
411
412 # This rule prevents make from using its default rules to try build
413
+14 -15
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -129,11 +129,10 @@
129129
}
130130
131131
# Options used to compile the included SQLite library.
132132
#
133133
set SQLITE_OPTIONS {
134
- -Dlocaltime=fossil_localtime
135134
-DSQLITE_OMIT_LOAD_EXTENSION=1
136135
-DSQLITE_ENABLE_LOCKING_STYLE=0
137136
-DSQLITE_THREADSAFE=0
138137
-DSQLITE_DEFAULT_FILE_FORMAT=4
139138
-DSQLITE_OMIT_DEPRECATED
@@ -147,11 +146,10 @@
147146
# Options used to compile the included SQLite shell.
148147
#
149148
set SHELL_OPTIONS {
150149
-Dmain=sqlite3_shell
151150
-DSQLITE_OMIT_LOAD_EXTENSION=1
152
- -Dsqlite3_strglob=strglob
153151
}
154152
155153
# Options used to compile the included SQLite shell on Windows.
156154
#
157155
set SHELL_WIN32_OPTIONS $SHELL_OPTIONS
@@ -263,11 +261,11 @@
263261
# The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
264262
# to 1. If it is set to 1, then there is no need to build or link
265263
# the sqlite3.o object. Instead, the system sqlite will be linked
266264
# using -lsqlite3.
267265
SQLITE3_OBJ.1 =
268
-SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
266
+SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o
269267
SQLITE3_OBJ. = $(SQLITE3_OBJ.0)
270268
271269
# The FOSSIL_ENABLE_TCL variable may be undefined, set to 0, or set to 1.
272270
# If it is set to 1, then we need to build the Tcl integration code and
273271
# link to the Tcl library.
@@ -275,11 +273,10 @@
275273
TCL_OBJ.1 = $(OBJDIR)/th_tcl.o
276274
TCL_OBJ. = $(TCL_OBJ.0)
277275
278276
EXTRAOBJ = \
279277
$(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) \
280
- $(OBJDIR)/shell.o \
281278
$(OBJDIR)/th.o \
282279
$(OBJDIR)/th_lang.o \
283280
$(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) \
284281
$(OBJDIR)/cson_amalgamation.o
285282
@@ -449,12 +446,12 @@
449446
#### The directories where the OpenSSL include and library files are located.
450447
# The recommended usage here is to use the Sysinternals junction tool
451448
# to create a hard link between an "openssl-1.x" sub-directory of the
452449
# Fossil source code directory and the target OpenSSL source directory.
453450
#
454
-OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1e/include
455
-OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1e
451
+OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1f/include
452
+OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1f
456453
457454
#### Either the directory where the Tcl library is installed or the Tcl
458455
# source code directory resides (depending on the value of the macro
459456
# FOSSIL_TCL_SOURCE). If this points to the Tcl install directory,
460457
# this directory must have "include" and "lib" sub-directories. If
@@ -806,18 +803,18 @@
806803
set j " \\\n "
807804
writeln "SQLITE_OPTIONS = [join $MINGW_SQLITE_OPTIONS $j]\n"
808805
set j " \\\n "
809806
writeln "SHELL_OPTIONS = [join $SHELL_WIN32_OPTIONS $j]\n"
810807
811
-writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c"
808
+writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c win/Makefile.mingw"
812809
writeln "\t\$(XTCC) \$(SQLITE_OPTIONS) \$(SQLITE_CFLAGS) -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n"
813810
814811
writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c"
815812
writeln "\t\$(XTCC) -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o\n"
816813
writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/jsos_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_status.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n"
817814
818
-writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h"
815
+writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h win/Makefile.mingw"
819816
writeln "\t\$(XTCC) \$(SHELL_OPTIONS) \$(SHELL_CFLAGS) -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n"
820817
821818
writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
822819
writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
823820
@@ -1019,12 +1016,12 @@
10191016
10201017
# Uncomment to enable SSL support
10211018
# FOSSIL_ENABLE_SSL = 1
10221019
10231020
!ifdef FOSSIL_ENABLE_SSL
1024
-SSLINCDIR = $(B)\compat\openssl-1.0.1e\include
1025
-SSLLIBDIR = $(B)\compat\openssl-1.0.1e\out32
1021
+SSLINCDIR = $(B)\compat\openssl-1.0.1f\include
1022
+SSLLIBDIR = $(B)\compat\openssl-1.0.1f\out32
10261023
SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib
10271024
!endif
10281025
10291026
# zlib options
10301027
ZINCDIR = $(B)\compat\zlib
@@ -1035,16 +1032,18 @@
10351032
10361033
!ifdef FOSSIL_ENABLE_SSL
10371034
INCL = $(INCL) -I$(SSLINCDIR)
10381035
!endif
10391036
1040
-CFLAGS = -nologo -MT -O2
1037
+CFLAGS = -nologo
10411038
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
10421039
10431040
!ifdef DEBUG
1044
-CFLAGS = $(CFLAGS) -Zi
1041
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
10451042
LDFLAGS = $(LDFLAGS) /DEBUG
1043
+!else
1044
+CFLAGS = $(CFLAGS) -MT -O2
10461045
!endif
10471046
10481047
BCC = $(CC) $(CFLAGS)
10491048
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
10501049
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1130,15 +1129,15 @@
11301129
$(BCC) $**
11311130
11321131
mkversion$E: $B\src\mkversion.c
11331132
$(BCC) $**
11341133
1135
-$(OX)\shell$O : $(SRCDIR)\shell.c
1134
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
11361135
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
11371136
1138
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c
1139
- $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $**
1137
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
1138
+ $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
11401139
11411140
$(OX)\th$O : $(SRCDIR)\th.c
11421141
$(TCC) /Fo$@ -c $**
11431142
11441143
$(OX)\th_lang$O : $(SRCDIR)\th_lang.c
11451144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -129,11 +129,10 @@
129 }
130
131 # Options used to compile the included SQLite library.
132 #
133 set SQLITE_OPTIONS {
134 -Dlocaltime=fossil_localtime
135 -DSQLITE_OMIT_LOAD_EXTENSION=1
136 -DSQLITE_ENABLE_LOCKING_STYLE=0
137 -DSQLITE_THREADSAFE=0
138 -DSQLITE_DEFAULT_FILE_FORMAT=4
139 -DSQLITE_OMIT_DEPRECATED
@@ -147,11 +146,10 @@
147 # Options used to compile the included SQLite shell.
148 #
149 set SHELL_OPTIONS {
150 -Dmain=sqlite3_shell
151 -DSQLITE_OMIT_LOAD_EXTENSION=1
152 -Dsqlite3_strglob=strglob
153 }
154
155 # Options used to compile the included SQLite shell on Windows.
156 #
157 set SHELL_WIN32_OPTIONS $SHELL_OPTIONS
@@ -263,11 +261,11 @@
263 # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
264 # to 1. If it is set to 1, then there is no need to build or link
265 # the sqlite3.o object. Instead, the system sqlite will be linked
266 # using -lsqlite3.
267 SQLITE3_OBJ.1 =
268 SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
269 SQLITE3_OBJ. = $(SQLITE3_OBJ.0)
270
271 # The FOSSIL_ENABLE_TCL variable may be undefined, set to 0, or set to 1.
272 # If it is set to 1, then we need to build the Tcl integration code and
273 # link to the Tcl library.
@@ -275,11 +273,10 @@
275 TCL_OBJ.1 = $(OBJDIR)/th_tcl.o
276 TCL_OBJ. = $(TCL_OBJ.0)
277
278 EXTRAOBJ = \
279 $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) \
280 $(OBJDIR)/shell.o \
281 $(OBJDIR)/th.o \
282 $(OBJDIR)/th_lang.o \
283 $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) \
284 $(OBJDIR)/cson_amalgamation.o
285
@@ -449,12 +446,12 @@
449 #### The directories where the OpenSSL include and library files are located.
450 # The recommended usage here is to use the Sysinternals junction tool
451 # to create a hard link between an "openssl-1.x" sub-directory of the
452 # Fossil source code directory and the target OpenSSL source directory.
453 #
454 OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1e/include
455 OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1e
456
457 #### Either the directory where the Tcl library is installed or the Tcl
458 # source code directory resides (depending on the value of the macro
459 # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory,
460 # this directory must have "include" and "lib" sub-directories. If
@@ -806,18 +803,18 @@
806 set j " \\\n "
807 writeln "SQLITE_OPTIONS = [join $MINGW_SQLITE_OPTIONS $j]\n"
808 set j " \\\n "
809 writeln "SHELL_OPTIONS = [join $SHELL_WIN32_OPTIONS $j]\n"
810
811 writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c"
812 writeln "\t\$(XTCC) \$(SQLITE_OPTIONS) \$(SQLITE_CFLAGS) -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n"
813
814 writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c"
815 writeln "\t\$(XTCC) -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o\n"
816 writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/jsos_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_status.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n"
817
818 writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h"
819 writeln "\t\$(XTCC) \$(SHELL_OPTIONS) \$(SHELL_CFLAGS) -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n"
820
821 writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
822 writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
823
@@ -1019,12 +1016,12 @@
1019
1020 # Uncomment to enable SSL support
1021 # FOSSIL_ENABLE_SSL = 1
1022
1023 !ifdef FOSSIL_ENABLE_SSL
1024 SSLINCDIR = $(B)\compat\openssl-1.0.1e\include
1025 SSLLIBDIR = $(B)\compat\openssl-1.0.1e\out32
1026 SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib
1027 !endif
1028
1029 # zlib options
1030 ZINCDIR = $(B)\compat\zlib
@@ -1035,16 +1032,18 @@
1035
1036 !ifdef FOSSIL_ENABLE_SSL
1037 INCL = $(INCL) -I$(SSLINCDIR)
1038 !endif
1039
1040 CFLAGS = -nologo -MT -O2
1041 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1042
1043 !ifdef DEBUG
1044 CFLAGS = $(CFLAGS) -Zi
1045 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
1046 !endif
1047
1048 BCC = $(CC) $(CFLAGS)
1049 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1050 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1130,15 +1129,15 @@
1130 $(BCC) $**
1131
1132 mkversion$E: $B\src\mkversion.c
1133 $(BCC) $**
1134
1135 $(OX)\shell$O : $(SRCDIR)\shell.c
1136 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1137
1138 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c
1139 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $**
1140
1141 $(OX)\th$O : $(SRCDIR)\th.c
1142 $(TCC) /Fo$@ -c $**
1143
1144 $(OX)\th_lang$O : $(SRCDIR)\th_lang.c
1145
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -129,11 +129,10 @@
129 }
130
131 # Options used to compile the included SQLite library.
132 #
133 set SQLITE_OPTIONS {
 
134 -DSQLITE_OMIT_LOAD_EXTENSION=1
135 -DSQLITE_ENABLE_LOCKING_STYLE=0
136 -DSQLITE_THREADSAFE=0
137 -DSQLITE_DEFAULT_FILE_FORMAT=4
138 -DSQLITE_OMIT_DEPRECATED
@@ -147,11 +146,10 @@
146 # Options used to compile the included SQLite shell.
147 #
148 set SHELL_OPTIONS {
149 -Dmain=sqlite3_shell
150 -DSQLITE_OMIT_LOAD_EXTENSION=1
 
151 }
152
153 # Options used to compile the included SQLite shell on Windows.
154 #
155 set SHELL_WIN32_OPTIONS $SHELL_OPTIONS
@@ -263,11 +261,11 @@
261 # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
262 # to 1. If it is set to 1, then there is no need to build or link
263 # the sqlite3.o object. Instead, the system sqlite will be linked
264 # using -lsqlite3.
265 SQLITE3_OBJ.1 =
266 SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o
267 SQLITE3_OBJ. = $(SQLITE3_OBJ.0)
268
269 # The FOSSIL_ENABLE_TCL variable may be undefined, set to 0, or set to 1.
270 # If it is set to 1, then we need to build the Tcl integration code and
271 # link to the Tcl library.
@@ -275,11 +273,10 @@
273 TCL_OBJ.1 = $(OBJDIR)/th_tcl.o
274 TCL_OBJ. = $(TCL_OBJ.0)
275
276 EXTRAOBJ = \
277 $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) \
 
278 $(OBJDIR)/th.o \
279 $(OBJDIR)/th_lang.o \
280 $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) \
281 $(OBJDIR)/cson_amalgamation.o
282
@@ -449,12 +446,12 @@
446 #### The directories where the OpenSSL include and library files are located.
447 # The recommended usage here is to use the Sysinternals junction tool
448 # to create a hard link between an "openssl-1.x" sub-directory of the
449 # Fossil source code directory and the target OpenSSL source directory.
450 #
451 OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1f/include
452 OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1f
453
454 #### Either the directory where the Tcl library is installed or the Tcl
455 # source code directory resides (depending on the value of the macro
456 # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory,
457 # this directory must have "include" and "lib" sub-directories. If
@@ -806,18 +803,18 @@
803 set j " \\\n "
804 writeln "SQLITE_OPTIONS = [join $MINGW_SQLITE_OPTIONS $j]\n"
805 set j " \\\n "
806 writeln "SHELL_OPTIONS = [join $SHELL_WIN32_OPTIONS $j]\n"
807
808 writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c win/Makefile.mingw"
809 writeln "\t\$(XTCC) \$(SQLITE_OPTIONS) \$(SQLITE_CFLAGS) -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n"
810
811 writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c"
812 writeln "\t\$(XTCC) -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o\n"
813 writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/jsos_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_status.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n"
814
815 writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h win/Makefile.mingw"
816 writeln "\t\$(XTCC) \$(SHELL_OPTIONS) \$(SHELL_CFLAGS) -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n"
817
818 writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
819 writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
820
@@ -1019,12 +1016,12 @@
1016
1017 # Uncomment to enable SSL support
1018 # FOSSIL_ENABLE_SSL = 1
1019
1020 !ifdef FOSSIL_ENABLE_SSL
1021 SSLINCDIR = $(B)\compat\openssl-1.0.1f\include
1022 SSLLIBDIR = $(B)\compat\openssl-1.0.1f\out32
1023 SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib
1024 !endif
1025
1026 # zlib options
1027 ZINCDIR = $(B)\compat\zlib
@@ -1035,16 +1032,18 @@
1032
1033 !ifdef FOSSIL_ENABLE_SSL
1034 INCL = $(INCL) -I$(SSLINCDIR)
1035 !endif
1036
1037 CFLAGS = -nologo
1038 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1039
1040 !ifdef DEBUG
1041 CFLAGS = $(CFLAGS) -Zi -MTd -Od
1042 LDFLAGS = $(LDFLAGS) /DEBUG
1043 !else
1044 CFLAGS = $(CFLAGS) -MT -O2
1045 !endif
1046
1047 BCC = $(CC) $(CFLAGS)
1048 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1049 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1130,15 +1129,15 @@
1129 $(BCC) $**
1130
1131 mkversion$E: $B\src\mkversion.c
1132 $(BCC) $**
1133
1134 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
1135 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1136
1137 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
1138 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1139
1140 $(OX)\th$O : $(SRCDIR)\th.c
1141 $(TCC) /Fo$@ -c $**
1142
1143 $(OX)\th_lang$O : $(SRCDIR)\th_lang.c
1144
+34 -16
--- src/manifest.c
+++ src/manifest.c
@@ -1494,18 +1494,30 @@
14941494
#endif /* LOCAL_INTERFACE */
14951495
14961496
/*
14971497
** Finish up a sequence of manifest_crosslink calls.
14981498
*/
1499
-void manifest_crosslink_end(void){
1499
+int manifest_crosslink_end(int flags){
15001500
Stmt q, u;
15011501
int i;
1502
+ int rc = TH_OK;
1503
+ int permitHooks = (flags & MC_PERMIT_HOOKS);
1504
+ const char *zScript = 0;
15021505
assert( manifest_crosslink_busy==1 );
1506
+ if( permitHooks ){
1507
+ rc = xfer_run_common_script();
1508
+ if( rc==TH_OK ){
1509
+ zScript = xfer_ticket_code();
1510
+ }
1511
+ }
15031512
db_prepare(&q, "SELECT uuid FROM pending_tkt");
15041513
while( db_step(&q)==SQLITE_ROW ){
15051514
const char *zUuid = db_column_text(&q, 0);
15061515
ticket_rebuild_entry(zUuid);
1516
+ if( permitHooks && rc==TH_OK ){
1517
+ rc = xfer_run_script(zScript, zUuid);
1518
+ }
15071519
}
15081520
db_finalize(&q);
15091521
db_multi_exec("DROP TABLE pending_tkt");
15101522
15111523
/* If multiple check-ins happen close together in time, adjust their
@@ -1528,18 +1540,21 @@
15281540
db_step(&u);
15291541
db_reset(&u);
15301542
}
15311543
db_finalize(&q);
15321544
db_finalize(&u);
1533
- db_multi_exec(
1534
- "UPDATE event SET mtime=(SELECT m1 FROM time_fudge WHERE mid=objid)"
1535
- " WHERE objid IN (SELECT mid FROM time_fudge);"
1536
- "DROP TABLE time_fudge;"
1537
- );
1545
+ if( db_exists("SELECT 1 FROM time_fudge") ){
1546
+ db_multi_exec(
1547
+ "UPDATE event SET mtime=(SELECT m1 FROM time_fudge WHERE mid=objid)"
1548
+ " WHERE objid IN (SELECT mid FROM time_fudge);"
1549
+ );
1550
+ }
1551
+ db_multi_exec("DROP TABLE time_fudge;");
15381552
15391553
db_end_transaction(0);
15401554
manifest_crosslink_busy = 0;
1555
+ return ( rc!=TH_ERROR );
15411556
}
15421557
15431558
/*
15441559
** Make an entry in the event table for a ticket change artifact.
15451560
*/
@@ -1657,14 +1672,15 @@
16571672
** Processing for other control artifacts was added later. The name
16581673
** of the routine, "manifest_crosslink", and the name of this source
16591674
** file, is a legacy of its original use.
16601675
*/
16611676
int manifest_crosslink(int rid, Blob *pContent, int flags){
1662
- int i, result = TH_OK;
1677
+ int i, rc = TH_OK;
16631678
Manifest *p;
16641679
Stmt q;
16651680
int parentid = 0;
1681
+ int permitHooks = (flags & MC_PERMIT_HOOKS);
16661682
const char *zScript = 0;
16671683
const char *zUuid = 0;
16681684
16691685
if( (p = manifest_cache_find(rid))!=0 ){
16701686
blob_reset(pContent);
@@ -1685,11 +1701,13 @@
16851701
fossil_error(1, "cannot fetch baseline manifest");
16861702
return 0;
16871703
}
16881704
db_begin_transaction();
16891705
if( p->type==CFTYPE_MANIFEST ){
1690
- zScript = xfer_commit_code();
1706
+ if( permitHooks ){
1707
+ zScript = xfer_commit_code();
1708
+ }
16911709
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
16921710
if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
16931711
char *zCom;
16941712
for(i=0; i<p->nParent; i++){
16951713
int pid = uuid_to_rid(p->azParent[i], 1);
@@ -1883,12 +1901,10 @@
18831901
}
18841902
}
18851903
if( p->type==CFTYPE_TICKET ){
18861904
char *zTag;
18871905
1888
- zScript = xfer_ticket_code();
1889
- zUuid = p->zTicketUuid;
18901906
assert( manifest_crosslink_busy==1 );
18911907
zTag = mprintf("tkt-%s", p->zTicketUuid);
18921908
tag_insert(zTag, 1, 0, rid, p->rDate, rid);
18931909
free(zTag);
18941910
db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
@@ -1968,11 +1984,13 @@
19681984
zTagUuid);
19691985
branchMove = 0;
19701986
if( db_exists("SELECT 1 FROM event, blob"
19711987
" WHERE event.type='ci' AND event.objid=blob.rid"
19721988
" AND blob.uuid='%s'", zTagUuid) ){
1973
- zScript = xfer_commit_code();
1989
+ if( permitHooks ){
1990
+ zScript = xfer_commit_code();
1991
+ }
19741992
zUuid = zTagUuid;
19751993
}
19761994
}
19771995
zName = p->aTag[i].zName;
19781996
zValue = p->aTag[i].zValue;
@@ -2042,23 +2060,23 @@
20422060
p->rDate, rid, p->zUser, blob_str(&comment)+1
20432061
);
20442062
blob_reset(&comment);
20452063
}
20462064
db_end_transaction(0);
2047
- if( zScript && (flags & MC_PERMIT_HOOKS) ){
2048
- result = xfer_run_common_script();
2049
- if( result==TH_OK ){
2050
- result = xfer_run_script(zScript, zUuid);
2065
+ if( permitHooks ){
2066
+ rc = xfer_run_common_script();
2067
+ if( rc==TH_OK ){
2068
+ rc = xfer_run_script(zScript, zUuid);
20512069
}
20522070
}
20532071
if( p->type==CFTYPE_MANIFEST ){
20542072
manifest_cache_insert(p);
20552073
}else{
20562074
manifest_destroy(p);
20572075
}
20582076
assert( blob_is_reset(pContent) );
2059
- return ( result!=TH_ERROR );
2077
+ return ( rc!=TH_ERROR );
20602078
}
20612079
20622080
/*
20632081
** COMMAND: test-crosslink
20642082
**
20652083
--- src/manifest.c
+++ src/manifest.c
@@ -1494,18 +1494,30 @@
1494 #endif /* LOCAL_INTERFACE */
1495
1496 /*
1497 ** Finish up a sequence of manifest_crosslink calls.
1498 */
1499 void manifest_crosslink_end(void){
1500 Stmt q, u;
1501 int i;
 
 
 
1502 assert( manifest_crosslink_busy==1 );
 
 
 
 
 
 
1503 db_prepare(&q, "SELECT uuid FROM pending_tkt");
1504 while( db_step(&q)==SQLITE_ROW ){
1505 const char *zUuid = db_column_text(&q, 0);
1506 ticket_rebuild_entry(zUuid);
 
 
 
1507 }
1508 db_finalize(&q);
1509 db_multi_exec("DROP TABLE pending_tkt");
1510
1511 /* If multiple check-ins happen close together in time, adjust their
@@ -1528,18 +1540,21 @@
1528 db_step(&u);
1529 db_reset(&u);
1530 }
1531 db_finalize(&q);
1532 db_finalize(&u);
1533 db_multi_exec(
1534 "UPDATE event SET mtime=(SELECT m1 FROM time_fudge WHERE mid=objid)"
1535 " WHERE objid IN (SELECT mid FROM time_fudge);"
1536 "DROP TABLE time_fudge;"
1537 );
 
 
1538
1539 db_end_transaction(0);
1540 manifest_crosslink_busy = 0;
 
1541 }
1542
1543 /*
1544 ** Make an entry in the event table for a ticket change artifact.
1545 */
@@ -1657,14 +1672,15 @@
1657 ** Processing for other control artifacts was added later. The name
1658 ** of the routine, "manifest_crosslink", and the name of this source
1659 ** file, is a legacy of its original use.
1660 */
1661 int manifest_crosslink(int rid, Blob *pContent, int flags){
1662 int i, result = TH_OK;
1663 Manifest *p;
1664 Stmt q;
1665 int parentid = 0;
 
1666 const char *zScript = 0;
1667 const char *zUuid = 0;
1668
1669 if( (p = manifest_cache_find(rid))!=0 ){
1670 blob_reset(pContent);
@@ -1685,11 +1701,13 @@
1685 fossil_error(1, "cannot fetch baseline manifest");
1686 return 0;
1687 }
1688 db_begin_transaction();
1689 if( p->type==CFTYPE_MANIFEST ){
1690 zScript = xfer_commit_code();
 
 
1691 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
1692 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
1693 char *zCom;
1694 for(i=0; i<p->nParent; i++){
1695 int pid = uuid_to_rid(p->azParent[i], 1);
@@ -1883,12 +1901,10 @@
1883 }
1884 }
1885 if( p->type==CFTYPE_TICKET ){
1886 char *zTag;
1887
1888 zScript = xfer_ticket_code();
1889 zUuid = p->zTicketUuid;
1890 assert( manifest_crosslink_busy==1 );
1891 zTag = mprintf("tkt-%s", p->zTicketUuid);
1892 tag_insert(zTag, 1, 0, rid, p->rDate, rid);
1893 free(zTag);
1894 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
@@ -1968,11 +1984,13 @@
1968 zTagUuid);
1969 branchMove = 0;
1970 if( db_exists("SELECT 1 FROM event, blob"
1971 " WHERE event.type='ci' AND event.objid=blob.rid"
1972 " AND blob.uuid='%s'", zTagUuid) ){
1973 zScript = xfer_commit_code();
 
 
1974 zUuid = zTagUuid;
1975 }
1976 }
1977 zName = p->aTag[i].zName;
1978 zValue = p->aTag[i].zValue;
@@ -2042,23 +2060,23 @@
2042 p->rDate, rid, p->zUser, blob_str(&comment)+1
2043 );
2044 blob_reset(&comment);
2045 }
2046 db_end_transaction(0);
2047 if( zScript && (flags & MC_PERMIT_HOOKS) ){
2048 result = xfer_run_common_script();
2049 if( result==TH_OK ){
2050 result = xfer_run_script(zScript, zUuid);
2051 }
2052 }
2053 if( p->type==CFTYPE_MANIFEST ){
2054 manifest_cache_insert(p);
2055 }else{
2056 manifest_destroy(p);
2057 }
2058 assert( blob_is_reset(pContent) );
2059 return ( result!=TH_ERROR );
2060 }
2061
2062 /*
2063 ** COMMAND: test-crosslink
2064 **
2065
--- src/manifest.c
+++ src/manifest.c
@@ -1494,18 +1494,30 @@
1494 #endif /* LOCAL_INTERFACE */
1495
1496 /*
1497 ** Finish up a sequence of manifest_crosslink calls.
1498 */
1499 int manifest_crosslink_end(int flags){
1500 Stmt q, u;
1501 int i;
1502 int rc = TH_OK;
1503 int permitHooks = (flags & MC_PERMIT_HOOKS);
1504 const char *zScript = 0;
1505 assert( manifest_crosslink_busy==1 );
1506 if( permitHooks ){
1507 rc = xfer_run_common_script();
1508 if( rc==TH_OK ){
1509 zScript = xfer_ticket_code();
1510 }
1511 }
1512 db_prepare(&q, "SELECT uuid FROM pending_tkt");
1513 while( db_step(&q)==SQLITE_ROW ){
1514 const char *zUuid = db_column_text(&q, 0);
1515 ticket_rebuild_entry(zUuid);
1516 if( permitHooks && rc==TH_OK ){
1517 rc = xfer_run_script(zScript, zUuid);
1518 }
1519 }
1520 db_finalize(&q);
1521 db_multi_exec("DROP TABLE pending_tkt");
1522
1523 /* If multiple check-ins happen close together in time, adjust their
@@ -1528,18 +1540,21 @@
1540 db_step(&u);
1541 db_reset(&u);
1542 }
1543 db_finalize(&q);
1544 db_finalize(&u);
1545 if( db_exists("SELECT 1 FROM time_fudge") ){
1546 db_multi_exec(
1547 "UPDATE event SET mtime=(SELECT m1 FROM time_fudge WHERE mid=objid)"
1548 " WHERE objid IN (SELECT mid FROM time_fudge);"
1549 );
1550 }
1551 db_multi_exec("DROP TABLE time_fudge;");
1552
1553 db_end_transaction(0);
1554 manifest_crosslink_busy = 0;
1555 return ( rc!=TH_ERROR );
1556 }
1557
1558 /*
1559 ** Make an entry in the event table for a ticket change artifact.
1560 */
@@ -1657,14 +1672,15 @@
1672 ** Processing for other control artifacts was added later. The name
1673 ** of the routine, "manifest_crosslink", and the name of this source
1674 ** file, is a legacy of its original use.
1675 */
1676 int manifest_crosslink(int rid, Blob *pContent, int flags){
1677 int i, rc = TH_OK;
1678 Manifest *p;
1679 Stmt q;
1680 int parentid = 0;
1681 int permitHooks = (flags & MC_PERMIT_HOOKS);
1682 const char *zScript = 0;
1683 const char *zUuid = 0;
1684
1685 if( (p = manifest_cache_find(rid))!=0 ){
1686 blob_reset(pContent);
@@ -1685,11 +1701,13 @@
1701 fossil_error(1, "cannot fetch baseline manifest");
1702 return 0;
1703 }
1704 db_begin_transaction();
1705 if( p->type==CFTYPE_MANIFEST ){
1706 if( permitHooks ){
1707 zScript = xfer_commit_code();
1708 }
1709 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
1710 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
1711 char *zCom;
1712 for(i=0; i<p->nParent; i++){
1713 int pid = uuid_to_rid(p->azParent[i], 1);
@@ -1883,12 +1901,10 @@
1901 }
1902 }
1903 if( p->type==CFTYPE_TICKET ){
1904 char *zTag;
1905
 
 
1906 assert( manifest_crosslink_busy==1 );
1907 zTag = mprintf("tkt-%s", p->zTicketUuid);
1908 tag_insert(zTag, 1, 0, rid, p->rDate, rid);
1909 free(zTag);
1910 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
@@ -1968,11 +1984,13 @@
1984 zTagUuid);
1985 branchMove = 0;
1986 if( db_exists("SELECT 1 FROM event, blob"
1987 " WHERE event.type='ci' AND event.objid=blob.rid"
1988 " AND blob.uuid='%s'", zTagUuid) ){
1989 if( permitHooks ){
1990 zScript = xfer_commit_code();
1991 }
1992 zUuid = zTagUuid;
1993 }
1994 }
1995 zName = p->aTag[i].zName;
1996 zValue = p->aTag[i].zValue;
@@ -2042,23 +2060,23 @@
2060 p->rDate, rid, p->zUser, blob_str(&comment)+1
2061 );
2062 blob_reset(&comment);
2063 }
2064 db_end_transaction(0);
2065 if( permitHooks ){
2066 rc = xfer_run_common_script();
2067 if( rc==TH_OK ){
2068 rc = xfer_run_script(zScript, zUuid);
2069 }
2070 }
2071 if( p->type==CFTYPE_MANIFEST ){
2072 manifest_cache_insert(p);
2073 }else{
2074 manifest_destroy(p);
2075 }
2076 assert( blob_is_reset(pContent) );
2077 return ( rc!=TH_ERROR );
2078 }
2079
2080 /*
2081 ** COMMAND: test-crosslink
2082 **
2083
+4 -4
--- src/merge.c
+++ src/merge.c
@@ -26,17 +26,17 @@
2626
** Print information about a particular check-in.
2727
*/
2828
void print_checkin_description(int rid, int indent, const char *zLabel){
2929
Stmt q;
3030
db_prepare(&q,
31
- "SELECT datetime(mtime,'localtime'),"
31
+ "SELECT datetime(mtime%s),"
3232
" coalesce(euser,user), coalesce(ecomment,comment),"
3333
" (SELECT uuid FROM blob WHERE rid=%d),"
3434
" (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
3535
" WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
3636
" AND tagxref.rid=%d AND tagxref.tagtype>0)"
37
- " FROM event WHERE objid=%d", rid, rid, rid);
37
+ " FROM event WHERE objid=%d", timeline_utc(), rid, rid, rid);
3838
if( db_step(&q)==SQLITE_ROW ){
3939
const char *zTagList = db_column_text(&q, 4);
4040
char *zCom;
4141
if( zTagList && zTagList[0] ){
4242
zCom = mprintf("%s (%s)", db_column_text(&q, 2), zTagList);
@@ -195,16 +195,16 @@
195195
TAG_BRANCH, vid)
196196
);
197197
}
198198
db_prepare(&q,
199199
"SELECT blob.uuid,"
200
- " datetime(event.mtime,'localtime'),"
200
+ " datetime(event.mtime%s),"
201201
" coalesce(ecomment, comment),"
202202
" coalesce(euser, user)"
203203
" FROM event, blob"
204204
" WHERE event.objid=%d AND blob.rid=%d",
205
- mid, mid
205
+ timeline_utc(), mid, mid
206206
);
207207
if( db_step(&q)==SQLITE_ROW ){
208208
char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"",
209209
db_column_text(&q, 0), db_column_text(&q, 1),
210210
db_column_text(&q, 3), db_column_text(&q, 2));
211211
--- src/merge.c
+++ src/merge.c
@@ -26,17 +26,17 @@
26 ** Print information about a particular check-in.
27 */
28 void print_checkin_description(int rid, int indent, const char *zLabel){
29 Stmt q;
30 db_prepare(&q,
31 "SELECT datetime(mtime,'localtime'),"
32 " coalesce(euser,user), coalesce(ecomment,comment),"
33 " (SELECT uuid FROM blob WHERE rid=%d),"
34 " (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
35 " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
36 " AND tagxref.rid=%d AND tagxref.tagtype>0)"
37 " FROM event WHERE objid=%d", rid, rid, rid);
38 if( db_step(&q)==SQLITE_ROW ){
39 const char *zTagList = db_column_text(&q, 4);
40 char *zCom;
41 if( zTagList && zTagList[0] ){
42 zCom = mprintf("%s (%s)", db_column_text(&q, 2), zTagList);
@@ -195,16 +195,16 @@
195 TAG_BRANCH, vid)
196 );
197 }
198 db_prepare(&q,
199 "SELECT blob.uuid,"
200 " datetime(event.mtime,'localtime'),"
201 " coalesce(ecomment, comment),"
202 " coalesce(euser, user)"
203 " FROM event, blob"
204 " WHERE event.objid=%d AND blob.rid=%d",
205 mid, mid
206 );
207 if( db_step(&q)==SQLITE_ROW ){
208 char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"",
209 db_column_text(&q, 0), db_column_text(&q, 1),
210 db_column_text(&q, 3), db_column_text(&q, 2));
211
--- src/merge.c
+++ src/merge.c
@@ -26,17 +26,17 @@
26 ** Print information about a particular check-in.
27 */
28 void print_checkin_description(int rid, int indent, const char *zLabel){
29 Stmt q;
30 db_prepare(&q,
31 "SELECT datetime(mtime%s),"
32 " coalesce(euser,user), coalesce(ecomment,comment),"
33 " (SELECT uuid FROM blob WHERE rid=%d),"
34 " (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
35 " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
36 " AND tagxref.rid=%d AND tagxref.tagtype>0)"
37 " FROM event WHERE objid=%d", timeline_utc(), rid, rid, rid);
38 if( db_step(&q)==SQLITE_ROW ){
39 const char *zTagList = db_column_text(&q, 4);
40 char *zCom;
41 if( zTagList && zTagList[0] ){
42 zCom = mprintf("%s (%s)", db_column_text(&q, 2), zTagList);
@@ -195,16 +195,16 @@
195 TAG_BRANCH, vid)
196 );
197 }
198 db_prepare(&q,
199 "SELECT blob.uuid,"
200 " datetime(event.mtime%s),"
201 " coalesce(ecomment, comment),"
202 " coalesce(euser, user)"
203 " FROM event, blob"
204 " WHERE event.objid=%d AND blob.rid=%d",
205 timeline_utc(), mid, mid
206 );
207 if( db_step(&q)==SQLITE_ROW ){
208 char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"",
209 db_column_text(&q, 0), db_column_text(&q, 1),
210 db_column_text(&q, 3), db_column_text(&q, 2));
211
+6 -6
--- src/name.c
+++ src/name.c
@@ -458,18 +458,18 @@
458458
}else if( rid==0 ){
459459
fossil_print("Unknown artifact: %s\n", zName);
460460
}else{
461461
Stmt q;
462462
db_prepare(&q,
463
- "SELECT uuid, size, datetime(mtime, 'localtime'), ipaddr,"
463
+ "SELECT uuid, size, datetime(mtime%s), ipaddr,"
464464
" (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
465465
" WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
466466
" AND tagxref.rid=blob.rid AND tagxref.tagtype>0)"
467467
" FROM blob, rcvfrom"
468468
" WHERE rid=%d"
469469
" AND rcvfrom.rcvid=blob.rcvid",
470
- rid);
470
+ timeline_utc(), rid);
471471
if( db_step(&q)==SQLITE_ROW ){
472472
const char *zTagList = db_column_text(&q, 4);
473473
if( verboseFlag ){
474474
fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid);
475475
fossil_print("size: %d bytes\n", db_column_int(&q,1));
@@ -484,13 +484,13 @@
484484
fossil_print("tags: %s\n", zTagList);
485485
}
486486
}
487487
db_finalize(&q);
488488
db_prepare(&q,
489
- "SELECT type, datetime(mtime,'localtime'),"
489
+ "SELECT type, datetime(mtime%s),"
490490
" coalesce(euser,user), coalesce(ecomment,comment)"
491
- " FROM event WHERE objid=%d", rid);
491
+ " FROM event WHERE objid=%d", timeline_utc(), rid);
492492
if( db_step(&q)==SQLITE_ROW ){
493493
const char *zType;
494494
switch( db_column_text(&q,0)[0] ){
495495
case 'c': zType = "Check-in"; break;
496496
case 'w': zType = "Wiki-edit"; break;
@@ -504,19 +504,19 @@
504504
fossil_print("comment: ");
505505
comment_print(db_column_text(&q,3), 10, 78);
506506
}
507507
db_finalize(&q);
508508
db_prepare(&q,
509
- "SELECT filename.name, blob.uuid, datetime(event.mtime,'localtime'),"
509
+ "SELECT filename.name, blob.uuid, datetime(event.mtime%s),"
510510
" coalesce(euser,user), coalesce(ecomment,comment)"
511511
" FROM mlink, filename, blob, event"
512512
" WHERE mlink.fid=%d"
513513
" AND filename.fnid=mlink.fnid"
514514
" AND event.objid=mlink.mid"
515515
" AND blob.rid=mlink.mid"
516516
" ORDER BY event.mtime DESC /*sort*/",
517
- rid);
517
+ timeline_utc(), rid);
518518
while( db_step(&q)==SQLITE_ROW ){
519519
fossil_print("file: %s\n", db_column_text(&q,0));
520520
fossil_print(" part of [%.10s] by %s on %s\n",
521521
db_column_text(&q, 1),
522522
db_column_text(&q, 3),
523523
--- src/name.c
+++ src/name.c
@@ -458,18 +458,18 @@
458 }else if( rid==0 ){
459 fossil_print("Unknown artifact: %s\n", zName);
460 }else{
461 Stmt q;
462 db_prepare(&q,
463 "SELECT uuid, size, datetime(mtime, 'localtime'), ipaddr,"
464 " (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
465 " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
466 " AND tagxref.rid=blob.rid AND tagxref.tagtype>0)"
467 " FROM blob, rcvfrom"
468 " WHERE rid=%d"
469 " AND rcvfrom.rcvid=blob.rcvid",
470 rid);
471 if( db_step(&q)==SQLITE_ROW ){
472 const char *zTagList = db_column_text(&q, 4);
473 if( verboseFlag ){
474 fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid);
475 fossil_print("size: %d bytes\n", db_column_int(&q,1));
@@ -484,13 +484,13 @@
484 fossil_print("tags: %s\n", zTagList);
485 }
486 }
487 db_finalize(&q);
488 db_prepare(&q,
489 "SELECT type, datetime(mtime,'localtime'),"
490 " coalesce(euser,user), coalesce(ecomment,comment)"
491 " FROM event WHERE objid=%d", rid);
492 if( db_step(&q)==SQLITE_ROW ){
493 const char *zType;
494 switch( db_column_text(&q,0)[0] ){
495 case 'c': zType = "Check-in"; break;
496 case 'w': zType = "Wiki-edit"; break;
@@ -504,19 +504,19 @@
504 fossil_print("comment: ");
505 comment_print(db_column_text(&q,3), 10, 78);
506 }
507 db_finalize(&q);
508 db_prepare(&q,
509 "SELECT filename.name, blob.uuid, datetime(event.mtime,'localtime'),"
510 " coalesce(euser,user), coalesce(ecomment,comment)"
511 " FROM mlink, filename, blob, event"
512 " WHERE mlink.fid=%d"
513 " AND filename.fnid=mlink.fnid"
514 " AND event.objid=mlink.mid"
515 " AND blob.rid=mlink.mid"
516 " ORDER BY event.mtime DESC /*sort*/",
517 rid);
518 while( db_step(&q)==SQLITE_ROW ){
519 fossil_print("file: %s\n", db_column_text(&q,0));
520 fossil_print(" part of [%.10s] by %s on %s\n",
521 db_column_text(&q, 1),
522 db_column_text(&q, 3),
523
--- src/name.c
+++ src/name.c
@@ -458,18 +458,18 @@
458 }else if( rid==0 ){
459 fossil_print("Unknown artifact: %s\n", zName);
460 }else{
461 Stmt q;
462 db_prepare(&q,
463 "SELECT uuid, size, datetime(mtime%s), ipaddr,"
464 " (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
465 " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
466 " AND tagxref.rid=blob.rid AND tagxref.tagtype>0)"
467 " FROM blob, rcvfrom"
468 " WHERE rid=%d"
469 " AND rcvfrom.rcvid=blob.rcvid",
470 timeline_utc(), rid);
471 if( db_step(&q)==SQLITE_ROW ){
472 const char *zTagList = db_column_text(&q, 4);
473 if( verboseFlag ){
474 fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid);
475 fossil_print("size: %d bytes\n", db_column_int(&q,1));
@@ -484,13 +484,13 @@
484 fossil_print("tags: %s\n", zTagList);
485 }
486 }
487 db_finalize(&q);
488 db_prepare(&q,
489 "SELECT type, datetime(mtime%s),"
490 " coalesce(euser,user), coalesce(ecomment,comment)"
491 " FROM event WHERE objid=%d", timeline_utc(), rid);
492 if( db_step(&q)==SQLITE_ROW ){
493 const char *zType;
494 switch( db_column_text(&q,0)[0] ){
495 case 'c': zType = "Check-in"; break;
496 case 'w': zType = "Wiki-edit"; break;
@@ -504,19 +504,19 @@
504 fossil_print("comment: ");
505 comment_print(db_column_text(&q,3), 10, 78);
506 }
507 db_finalize(&q);
508 db_prepare(&q,
509 "SELECT filename.name, blob.uuid, datetime(event.mtime%s),"
510 " coalesce(euser,user), coalesce(ecomment,comment)"
511 " FROM mlink, filename, blob, event"
512 " WHERE mlink.fid=%d"
513 " AND filename.fnid=mlink.fnid"
514 " AND event.objid=mlink.mid"
515 " AND blob.rid=mlink.mid"
516 " ORDER BY event.mtime DESC /*sort*/",
517 timeline_utc(), rid);
518 while( db_step(&q)==SQLITE_ROW ){
519 fossil_print("file: %s\n", db_column_text(&q,0));
520 fossil_print(" part of [%.10s] by %s on %s\n",
521 db_column_text(&q, 1),
522 db_column_text(&q, 3),
523
+1 -1
--- src/rebuild.c
+++ src/rebuild.c
@@ -412,11 +412,11 @@
412412
db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
413413
rebuild_step_done(rid);
414414
}
415415
}
416416
db_finalize(&s);
417
- manifest_crosslink_end();
417
+ manifest_crosslink_end(MC_NONE);
418418
rebuild_tag_trunk();
419419
if( ttyOutput && !g.fQuiet && totalSize>0 ){
420420
processCnt += incrSize;
421421
percent_complete((processCnt*1000)/totalSize);
422422
}
423423
--- src/rebuild.c
+++ src/rebuild.c
@@ -412,11 +412,11 @@
412 db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
413 rebuild_step_done(rid);
414 }
415 }
416 db_finalize(&s);
417 manifest_crosslink_end();
418 rebuild_tag_trunk();
419 if( ttyOutput && !g.fQuiet && totalSize>0 ){
420 processCnt += incrSize;
421 percent_complete((processCnt*1000)/totalSize);
422 }
423
--- src/rebuild.c
+++ src/rebuild.c
@@ -412,11 +412,11 @@
412 db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
413 rebuild_step_done(rid);
414 }
415 }
416 db_finalize(&s);
417 manifest_crosslink_end(MC_NONE);
418 rebuild_tag_trunk();
419 if( ttyOutput && !g.fQuiet && totalSize>0 ){
420 processCnt += incrSize;
421 percent_complete((processCnt*1000)/totalSize);
422 }
423
--- src/report.c
+++ src/report.c
@@ -210,11 +210,10 @@
210210
211211
/*
212212
** Activate the query authorizer
213213
*/
214214
static void report_restrict_sql(char **pzErr){
215
- (void)fossil_localtime(0);
216215
sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)pzErr);
217216
}
218217
static void report_unrestrict_sql(void){
219218
sqlite3_set_authorizer(g.db, 0, 0);
220219
}
221220
--- src/report.c
+++ src/report.c
@@ -210,11 +210,10 @@
210
211 /*
212 ** Activate the query authorizer
213 */
214 static void report_restrict_sql(char **pzErr){
215 (void)fossil_localtime(0);
216 sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)pzErr);
217 }
218 static void report_unrestrict_sql(void){
219 sqlite3_set_authorizer(g.db, 0, 0);
220 }
221
--- src/report.c
+++ src/report.c
@@ -210,11 +210,10 @@
210
211 /*
212 ** Activate the query authorizer
213 */
214 static void report_restrict_sql(char **pzErr){
 
215 sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)pzErr);
216 }
217 static void report_unrestrict_sql(void){
218 sqlite3_set_authorizer(g.db, 0, 0);
219 }
220
+20 -6
--- src/search.c
+++ src/search.c
@@ -106,10 +106,11 @@
106106
int iBonus = 0;
107107
int i, j;
108108
unsigned char seen[8];
109109
110110
memset(seen, 0, sizeof(seen));
111
+ if( zDoc==0 ) return score;
111112
for(i=0; zDoc[i]; i++){
112113
char c = zDoc[i];
113114
if( isBoundary[c&0xff] ) continue;
114115
for(j=0; j<p->nTerm; j++){
115116
int n = p->a[j].n;
@@ -166,21 +167,23 @@
166167
167168
/*
168169
** Testing the search function.
169170
**
170171
** COMMAND: search*
171
-** %fossil search [-all|-a] [-limit|-n #] pattern...
172
+** %fossil search [-all|-a] [-limit|-n #] [-width|-W #] pattern...
172173
**
173174
** Search for timeline entries matching all words
174175
** provided on the command line. Whole-word matches
175176
** scope more highly than partial matches.
176177
**
177178
** Outputs, by default, some top-N fraction of the
178179
** results. The -all option can be used to output
179180
** all matches, regardless of their search score.
180
-** -limit can be used to limit the number of entries
181
-** returned.
181
+** The -limit option can be used to limit the number
182
+** of entries returned. The -width option can be
183
+** used to set the output width used when printing
184
+** matches.
182185
*/
183186
void search_cmd(void){
184187
Search *p;
185188
Blob pattern;
186189
int i;
@@ -189,12 +192,22 @@
189192
int iBest;
190193
char fAll = NULL != find_option("all", "a", 0); /* If set, do not lop
191194
off the end of the
192195
results. */
193196
char const * zLimit = find_option("limit","n",1);
197
+ const char *zWidth = find_option("width","W",1);
194198
int nLimit = zLimit ? atoi(zLimit) : -1000; /* Max number of matching
195199
lines/entries to list */
200
+ int width;
201
+ if( zWidth ){
202
+ width = atoi(zWidth);
203
+ if( (width!=0) && (width<=20) ){
204
+ fossil_fatal("--width|-W value must be >20 or 0");
205
+ }
206
+ }else{
207
+ width = 79;
208
+ }
196209
197210
db_must_be_within_tree();
198211
if( g.argc<2 ) return;
199212
blob_init(&pattern, g.argv[2], -1);
200213
for(i=3; i<g.argc; i++){
@@ -206,15 +219,16 @@
206219
207220
db_multi_exec(
208221
"CREATE TEMP TABLE srch(rid,uuid,date,comment,x);"
209222
"CREATE INDEX srch_idx1 ON srch(x);"
210223
"INSERT INTO srch(rid,uuid,date,comment,x)"
211
- " SELECT blob.rid, uuid, datetime(event.mtime, 'localtime'),"
224
+ " SELECT blob.rid, uuid, datetime(event.mtime%s),"
212225
" coalesce(ecomment,comment),"
213226
" score(coalesce(ecomment,comment)) AS y"
214227
" FROM event, blob"
215
- " WHERE blob.rid=event.objid AND y>0;"
228
+ " WHERE blob.rid=event.objid AND y>0;",
229
+ timeline_utc()
216230
);
217231
iBest = db_int(0, "SELECT max(x) FROM srch");
218232
blob_append(&sql,
219233
"SELECT rid, uuid, date, comment, 0, 0 FROM srch "
220234
"WHERE 1 ", -1);
@@ -222,8 +236,8 @@
222236
blob_appendf(&sql,"AND x>%d ", iBest/3);
223237
}
224238
blob_append(&sql, "ORDER BY x DESC, date DESC ", -1);
225239
db_prepare(&q, blob_str(&sql));
226240
blob_reset(&sql);
227
- print_timeline(&q, nLimit, 79, 0);
241
+ print_timeline(&q, nLimit, width, 0);
228242
db_finalize(&q);
229243
}
230244
--- src/search.c
+++ src/search.c
@@ -106,10 +106,11 @@
106 int iBonus = 0;
107 int i, j;
108 unsigned char seen[8];
109
110 memset(seen, 0, sizeof(seen));
 
111 for(i=0; zDoc[i]; i++){
112 char c = zDoc[i];
113 if( isBoundary[c&0xff] ) continue;
114 for(j=0; j<p->nTerm; j++){
115 int n = p->a[j].n;
@@ -166,21 +167,23 @@
166
167 /*
168 ** Testing the search function.
169 **
170 ** COMMAND: search*
171 ** %fossil search [-all|-a] [-limit|-n #] pattern...
172 **
173 ** Search for timeline entries matching all words
174 ** provided on the command line. Whole-word matches
175 ** scope more highly than partial matches.
176 **
177 ** Outputs, by default, some top-N fraction of the
178 ** results. The -all option can be used to output
179 ** all matches, regardless of their search score.
180 ** -limit can be used to limit the number of entries
181 ** returned.
 
 
182 */
183 void search_cmd(void){
184 Search *p;
185 Blob pattern;
186 int i;
@@ -189,12 +192,22 @@
189 int iBest;
190 char fAll = NULL != find_option("all", "a", 0); /* If set, do not lop
191 off the end of the
192 results. */
193 char const * zLimit = find_option("limit","n",1);
 
194 int nLimit = zLimit ? atoi(zLimit) : -1000; /* Max number of matching
195 lines/entries to list */
 
 
 
 
 
 
 
 
 
196
197 db_must_be_within_tree();
198 if( g.argc<2 ) return;
199 blob_init(&pattern, g.argv[2], -1);
200 for(i=3; i<g.argc; i++){
@@ -206,15 +219,16 @@
206
207 db_multi_exec(
208 "CREATE TEMP TABLE srch(rid,uuid,date,comment,x);"
209 "CREATE INDEX srch_idx1 ON srch(x);"
210 "INSERT INTO srch(rid,uuid,date,comment,x)"
211 " SELECT blob.rid, uuid, datetime(event.mtime, 'localtime'),"
212 " coalesce(ecomment,comment),"
213 " score(coalesce(ecomment,comment)) AS y"
214 " FROM event, blob"
215 " WHERE blob.rid=event.objid AND y>0;"
 
216 );
217 iBest = db_int(0, "SELECT max(x) FROM srch");
218 blob_append(&sql,
219 "SELECT rid, uuid, date, comment, 0, 0 FROM srch "
220 "WHERE 1 ", -1);
@@ -222,8 +236,8 @@
222 blob_appendf(&sql,"AND x>%d ", iBest/3);
223 }
224 blob_append(&sql, "ORDER BY x DESC, date DESC ", -1);
225 db_prepare(&q, blob_str(&sql));
226 blob_reset(&sql);
227 print_timeline(&q, nLimit, 79, 0);
228 db_finalize(&q);
229 }
230
--- src/search.c
+++ src/search.c
@@ -106,10 +106,11 @@
106 int iBonus = 0;
107 int i, j;
108 unsigned char seen[8];
109
110 memset(seen, 0, sizeof(seen));
111 if( zDoc==0 ) return score;
112 for(i=0; zDoc[i]; i++){
113 char c = zDoc[i];
114 if( isBoundary[c&0xff] ) continue;
115 for(j=0; j<p->nTerm; j++){
116 int n = p->a[j].n;
@@ -166,21 +167,23 @@
167
168 /*
169 ** Testing the search function.
170 **
171 ** COMMAND: search*
172 ** %fossil search [-all|-a] [-limit|-n #] [-width|-W #] pattern...
173 **
174 ** Search for timeline entries matching all words
175 ** provided on the command line. Whole-word matches
176 ** scope more highly than partial matches.
177 **
178 ** Outputs, by default, some top-N fraction of the
179 ** results. The -all option can be used to output
180 ** all matches, regardless of their search score.
181 ** The -limit option can be used to limit the number
182 ** of entries returned. The -width option can be
183 ** used to set the output width used when printing
184 ** matches.
185 */
186 void search_cmd(void){
187 Search *p;
188 Blob pattern;
189 int i;
@@ -189,12 +192,22 @@
192 int iBest;
193 char fAll = NULL != find_option("all", "a", 0); /* If set, do not lop
194 off the end of the
195 results. */
196 char const * zLimit = find_option("limit","n",1);
197 const char *zWidth = find_option("width","W",1);
198 int nLimit = zLimit ? atoi(zLimit) : -1000; /* Max number of matching
199 lines/entries to list */
200 int width;
201 if( zWidth ){
202 width = atoi(zWidth);
203 if( (width!=0) && (width<=20) ){
204 fossil_fatal("--width|-W value must be >20 or 0");
205 }
206 }else{
207 width = 79;
208 }
209
210 db_must_be_within_tree();
211 if( g.argc<2 ) return;
212 blob_init(&pattern, g.argv[2], -1);
213 for(i=3; i<g.argc; i++){
@@ -206,15 +219,16 @@
219
220 db_multi_exec(
221 "CREATE TEMP TABLE srch(rid,uuid,date,comment,x);"
222 "CREATE INDEX srch_idx1 ON srch(x);"
223 "INSERT INTO srch(rid,uuid,date,comment,x)"
224 " SELECT blob.rid, uuid, datetime(event.mtime%s),"
225 " coalesce(ecomment,comment),"
226 " score(coalesce(ecomment,comment)) AS y"
227 " FROM event, blob"
228 " WHERE blob.rid=event.objid AND y>0;",
229 timeline_utc()
230 );
231 iBest = db_int(0, "SELECT max(x) FROM srch");
232 blob_append(&sql,
233 "SELECT rid, uuid, date, comment, 0, 0 FROM srch "
234 "WHERE 1 ", -1);
@@ -222,8 +236,8 @@
236 blob_appendf(&sql,"AND x>%d ", iBest/3);
237 }
238 blob_append(&sql, "ORDER BY x DESC, date DESC ", -1);
239 db_prepare(&q, blob_str(&sql));
240 blob_reset(&sql);
241 print_timeline(&q, nLimit, width, 0);
242 db_finalize(&q);
243 }
244
+4 -3
--- src/setup.c
+++ src/setup.c
@@ -1184,11 +1184,10 @@
11841184
@ <hr />
11851185
onoff_attribute("Use Universal Coordinated Time (UTC)",
11861186
"timeline-utc", "utc", 1, 0);
11871187
@ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
11881188
@ Zulu) instead of in local time. On this server, local time is currently
1189
- g.fTimeFormat = 2;
11901189
tmDiff = db_double(0.0, "SELECT julianday('now')");
11911190
tmDiff = db_double(0.0,
11921191
"SELECT (julianday(%.17g,'localtime')-julianday(%.17g))*24.0",
11931192
tmDiff, tmDiff);
11941193
sqlite3_snprintf(sizeof(zTmDiff), zTmDiff, "%.1f", tmDiff);
@@ -1598,13 +1597,15 @@
15981597
15991598
/*
16001599
** WEBPAGE: setup_logo
16011600
*/
16021601
void setup_logo(void){
1602
+ const char *zLogoMtime = db_get_mtime("logo-image", 0, 0);
16031603
const char *zLogoMime = db_get("logo-mimetype","image/gif");
16041604
const char *aLogoImg = P("logoim");
16051605
int szLogoImg = atoi(PD("logoim:bytes","0"));
1606
+ const char *zBgMtime = db_get_mtime("background-image", 0, 0);
16061607
const char *zBgMime = db_get("background-mimetype","image/gif");
16071608
const char *aBgImg = P("bgim");
16081609
int szBgImg = atoi(PD("bgim:bytes","0"));
16091610
if( szLogoImg>0 ){
16101611
zLogoMime = PD("logoim:mimetype","image/gif");
@@ -1668,11 +1669,11 @@
16681669
cgi_redirect("setup_logo");
16691670
}
16701671
style_header("Edit Project Logo And Background");
16711672
@ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b>
16721673
@ and looks like this:</p>
1673
- @ <blockquote><p><img src="%s(g.zTop)/logo" alt="logo" border="1" />
1674
+ @ <blockquote><p><img src="%s(g.zTop)/logo/%z(zLogoMtime)" alt="logo" border="1" />
16741675
@ </p></blockquote>
16751676
@
16761677
@ <form action="%s(g.zTop)/setup_logo" method="post"
16771678
@ enctype="multipart/form-data"><div>
16781679
@ <p>The logo is accessible to all users at this URL:
@@ -1690,11 +1691,11 @@
16901691
@ </div></form>
16911692
@ <hr />
16921693
@
16931694
@ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b>
16941695
@ and looks like this:</p>
1695
- @ <blockquote><p><img src="%s(g.zTop)/background" alt="background" border=1 />
1696
+ @ <blockquote><p><img src="%s(g.zTop)/background/%z(zBgMtime)" alt="background" border=1 />
16961697
@ </p></blockquote>
16971698
@
16981699
@ <form action="%s(g.zTop)/setup_logo" method="post"
16991700
@ enctype="multipart/form-data"><div>
17001701
@ <p>The background image is accessible to all users at this URL:
17011702
--- src/setup.c
+++ src/setup.c
@@ -1184,11 +1184,10 @@
1184 @ <hr />
1185 onoff_attribute("Use Universal Coordinated Time (UTC)",
1186 "timeline-utc", "utc", 1, 0);
1187 @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
1188 @ Zulu) instead of in local time. On this server, local time is currently
1189 g.fTimeFormat = 2;
1190 tmDiff = db_double(0.0, "SELECT julianday('now')");
1191 tmDiff = db_double(0.0,
1192 "SELECT (julianday(%.17g,'localtime')-julianday(%.17g))*24.0",
1193 tmDiff, tmDiff);
1194 sqlite3_snprintf(sizeof(zTmDiff), zTmDiff, "%.1f", tmDiff);
@@ -1598,13 +1597,15 @@
1598
1599 /*
1600 ** WEBPAGE: setup_logo
1601 */
1602 void setup_logo(void){
 
1603 const char *zLogoMime = db_get("logo-mimetype","image/gif");
1604 const char *aLogoImg = P("logoim");
1605 int szLogoImg = atoi(PD("logoim:bytes","0"));
 
1606 const char *zBgMime = db_get("background-mimetype","image/gif");
1607 const char *aBgImg = P("bgim");
1608 int szBgImg = atoi(PD("bgim:bytes","0"));
1609 if( szLogoImg>0 ){
1610 zLogoMime = PD("logoim:mimetype","image/gif");
@@ -1668,11 +1669,11 @@
1668 cgi_redirect("setup_logo");
1669 }
1670 style_header("Edit Project Logo And Background");
1671 @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b>
1672 @ and looks like this:</p>
1673 @ <blockquote><p><img src="%s(g.zTop)/logo" alt="logo" border="1" />
1674 @ </p></blockquote>
1675 @
1676 @ <form action="%s(g.zTop)/setup_logo" method="post"
1677 @ enctype="multipart/form-data"><div>
1678 @ <p>The logo is accessible to all users at this URL:
@@ -1690,11 +1691,11 @@
1690 @ </div></form>
1691 @ <hr />
1692 @
1693 @ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b>
1694 @ and looks like this:</p>
1695 @ <blockquote><p><img src="%s(g.zTop)/background" alt="background" border=1 />
1696 @ </p></blockquote>
1697 @
1698 @ <form action="%s(g.zTop)/setup_logo" method="post"
1699 @ enctype="multipart/form-data"><div>
1700 @ <p>The background image is accessible to all users at this URL:
1701
--- src/setup.c
+++ src/setup.c
@@ -1184,11 +1184,10 @@
1184 @ <hr />
1185 onoff_attribute("Use Universal Coordinated Time (UTC)",
1186 "timeline-utc", "utc", 1, 0);
1187 @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
1188 @ Zulu) instead of in local time. On this server, local time is currently
 
1189 tmDiff = db_double(0.0, "SELECT julianday('now')");
1190 tmDiff = db_double(0.0,
1191 "SELECT (julianday(%.17g,'localtime')-julianday(%.17g))*24.0",
1192 tmDiff, tmDiff);
1193 sqlite3_snprintf(sizeof(zTmDiff), zTmDiff, "%.1f", tmDiff);
@@ -1598,13 +1597,15 @@
1597
1598 /*
1599 ** WEBPAGE: setup_logo
1600 */
1601 void setup_logo(void){
1602 const char *zLogoMtime = db_get_mtime("logo-image", 0, 0);
1603 const char *zLogoMime = db_get("logo-mimetype","image/gif");
1604 const char *aLogoImg = P("logoim");
1605 int szLogoImg = atoi(PD("logoim:bytes","0"));
1606 const char *zBgMtime = db_get_mtime("background-image", 0, 0);
1607 const char *zBgMime = db_get("background-mimetype","image/gif");
1608 const char *aBgImg = P("bgim");
1609 int szBgImg = atoi(PD("bgim:bytes","0"));
1610 if( szLogoImg>0 ){
1611 zLogoMime = PD("logoim:mimetype","image/gif");
@@ -1668,11 +1669,11 @@
1669 cgi_redirect("setup_logo");
1670 }
1671 style_header("Edit Project Logo And Background");
1672 @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b>
1673 @ and looks like this:</p>
1674 @ <blockquote><p><img src="%s(g.zTop)/logo/%z(zLogoMtime)" alt="logo" border="1" />
1675 @ </p></blockquote>
1676 @
1677 @ <form action="%s(g.zTop)/setup_logo" method="post"
1678 @ enctype="multipart/form-data"><div>
1679 @ <p>The logo is accessible to all users at this URL:
@@ -1690,11 +1691,11 @@
1691 @ </div></form>
1692 @ <hr />
1693 @
1694 @ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b>
1695 @ and looks like this:</p>
1696 @ <blockquote><p><img src="%s(g.zTop)/background/%z(zBgMtime)" alt="background" border=1 />
1697 @ </p></blockquote>
1698 @
1699 @ <form action="%s(g.zTop)/setup_logo" method="post"
1700 @ enctype="multipart/form-data"><div>
1701 @ <p>The background image is accessible to all users at this URL:
1702
+13 -13
--- src/skins.c
+++ src/skins.c
@@ -171,11 +171,11 @@
171171
@ <head>
172172
@ <base href="$baseurl/$current_page" />
173173
@ <title>$<project_name>: $<title></title>
174174
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
175175
@ href="$home/timeline.rss">
176
-@ <link rel="stylesheet" href="$home/style.css?blackwhite" type="text/css"
176
+@ <link rel="stylesheet" href="$stylesheet_url" type="text/css"
177177
@ media="screen">
178178
@ </head>
179179
@ <body>
180180
@ <div class="header">
181181
@ <div class="title"><small>$<project_name></small><br />$<title></div>
@@ -192,11 +192,11 @@
192192
@ html "<a href=''$home$index_page''>Home</a>\n"
193193
@ if {[anycap jor]} {
194194
@ html "<a href=''$home/timeline''>Timeline</a>\n"
195195
@ }
196196
@ if {[hascap oh]} {
197
-@ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
197
+@ html "<a href=''$home/tree?ci=tip''>Files</a>\n"
198198
@ }
199199
@ if {[hascap o]} {
200200
@ html "<a href=''$home/brlist''>Branches</a>\n"
201201
@ html "<a href=''$home/taglist''>Tags</a>\n"
202202
@ }
@@ -379,11 +379,11 @@
379379
@ <head>
380380
@ <base href="$baseurl/$current_page" />
381381
@ <title>$<project_name>: $<title></title>
382382
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
383383
@ href="$home/timeline.rss">
384
-@ <link rel="stylesheet" href="$home/style.css?tan" type="text/css"
384
+@ <link rel="stylesheet" href="$stylesheet_url" type="text/css"
385385
@ media="screen">
386386
@ </head>
387387
@ <body>
388388
@ <div class="header">
389389
@ <div class="title">$<title></div>
@@ -402,11 +402,11 @@
402402
@ html "<a href=''$home$index_page''>Home</a>\n"
403403
@ if {[anycap jor]} {
404404
@ html "<a href=''$home/timeline''>Timeline</a>\n"
405405
@ }
406406
@ if {[hascap oh]} {
407
-@ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
407
+@ html "<a href=''$home/tree?ci=tip''>Files</a>\n"
408408
@ }
409409
@ if {[hascap o]} {
410410
@ html "<a href=''$home/brlist''>Branches</a>\n"
411411
@ html "<a href=''$home/taglist''>Tags</a>\n"
412412
@ }
@@ -620,17 +620,17 @@
620620
@ <head>
621621
@ <base href="$baseurl/$current_page" />
622622
@ <title>$<project_name>: $<title></title>
623623
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
624624
@ href="$home/timeline.rss">
625
-@ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
625
+@ <link rel="stylesheet" href="$stylesheet_url" type="text/css"
626626
@ media="screen">
627627
@ </head>
628628
@ <body>
629629
@ <div class="header">
630630
@ <div class="logo">
631
-@ <img src="$home/logo" alt="logo">
631
+@ <img src="$logo_image_url" alt="logo">
632632
@ <br />$<project_name>
633633
@ </div>
634634
@ <div class="title">$<title></div>
635635
@ <div class="status"><th1>
636636
@ if {[info exists login]} {
@@ -645,11 +645,11 @@
645645
@ html "<a href=''$home$index_page''>Home</a>\n"
646646
@ if {[anycap jor]} {
647647
@ html "<a href=''$home/timeline''>Timeline</a>\n"
648648
@ }
649649
@ if {[hascap oh]} {
650
-@ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
650
+@ html "<a href=''$home/tree?ci=tip''>Files</a>\n"
651651
@ }
652652
@ if {[hascap o]} {
653653
@ html "<a href=''$home/brlist''>Branches</a>\n"
654654
@ html "<a href=''$home/taglist''>Tags</a>\n"
655655
@ }
@@ -881,17 +881,17 @@
881881
@ <head>
882882
@ <base href="$baseurl/$current_page" />
883883
@ <title>$<project_name>: $<title></title>
884884
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
885885
@ href="$home/timeline.rss">
886
-@ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
886
+@ <link rel="stylesheet" href="$stylesheet_url" type="text/css"
887887
@ media="screen">
888888
@ </head>
889889
@ <body>
890890
@ <div class="header">
891891
@ <div class="logo">
892
-@ <img src="$home/logo" alt="logo">
892
+@ <img src="$logo_image_url" alt="logo">
893893
@ <br />$<project_name>
894894
@ </div>
895895
@ <div class="title">$<title></div>
896896
@ <div class="status"><th1>
897897
@ if {[info exists login]} {
@@ -906,11 +906,11 @@
906906
@ html "<a href=''$home$index_page''>Home</a>\n"
907907
@ if {[anycap jor]} {
908908
@ html "<a href=''$home/timeline''>Timeline</a>\n"
909909
@ }
910910
@ if {[hascap oh]} {
911
-@ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
911
+@ html "<a href=''$home/tree?ci=tip''>Files</a>\n"
912912
@ }
913913
@ if {[hascap o]} {
914914
@ html "<a href=''$home/brlist''>Branches</a>\n"
915915
@ html "<a href=''$home/taglist''>Tags</a>\n"
916916
@ }
@@ -1109,11 +1109,11 @@
11091109
@ <head>
11101110
@ <base href="$baseurl/$current_page" />
11111111
@ <title>$<project_name>: $<title></title>
11121112
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
11131113
@ href="$home/timeline.rss" />
1114
-@ <link rel="stylesheet" href="$home/style.css?enhanced" type="text/css"
1114
+@ <link rel="stylesheet" href="$stylesheet_url" type="text/css"
11151115
@ media="screen" />
11161116
@ </head>
11171117
@ <body>
11181118
@ <div class="header">
11191119
@ <div class="logo">
@@ -1176,11 +1176,11 @@
11761176
@ return $logourl
11771177
@ }
11781178
@ set logourl [getLogoUrl $baseurl]
11791179
@ </th1>
11801180
@ <a href="$logourl">
1181
-@ <img src="$baseurl/logo" border="0" alt="$project_name">
1181
+@ <img src="$logo_image_url" border="0" alt="$project_name">
11821182
@ </a>
11831183
@ </div>
11841184
@ <div class="title"><small>$<project_name></small><br />$<title></div>
11851185
@ <div class="status"><th1>
11861186
@ if {[info exists login]} {
@@ -1195,11 +1195,11 @@
11951195
@ html "<a href=''$home$index_page''>Home</a>\n"
11961196
@ if {[anycap jor]} {
11971197
@ html "<a href=''$home/timeline''>Timeline</a>\n"
11981198
@ }
11991199
@ if {[hascap oh]} {
1200
-@ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
1200
+@ html "<a href=''$home/tree?ci=tip''>Files</a>\n"
12011201
@ }
12021202
@ if {[hascap o]} {
12031203
@ html "<a href=''$home/brlist''>Branches</a>\n"
12041204
@ html "<a href=''$home/taglist''>Tags</a>\n"
12051205
@ }
12061206
--- src/skins.c
+++ src/skins.c
@@ -171,11 +171,11 @@
171 @ <head>
172 @ <base href="$baseurl/$current_page" />
173 @ <title>$<project_name>: $<title></title>
174 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
175 @ href="$home/timeline.rss">
176 @ <link rel="stylesheet" href="$home/style.css?blackwhite" type="text/css"
177 @ media="screen">
178 @ </head>
179 @ <body>
180 @ <div class="header">
181 @ <div class="title"><small>$<project_name></small><br />$<title></div>
@@ -192,11 +192,11 @@
192 @ html "<a href=''$home$index_page''>Home</a>\n"
193 @ if {[anycap jor]} {
194 @ html "<a href=''$home/timeline''>Timeline</a>\n"
195 @ }
196 @ if {[hascap oh]} {
197 @ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
198 @ }
199 @ if {[hascap o]} {
200 @ html "<a href=''$home/brlist''>Branches</a>\n"
201 @ html "<a href=''$home/taglist''>Tags</a>\n"
202 @ }
@@ -379,11 +379,11 @@
379 @ <head>
380 @ <base href="$baseurl/$current_page" />
381 @ <title>$<project_name>: $<title></title>
382 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
383 @ href="$home/timeline.rss">
384 @ <link rel="stylesheet" href="$home/style.css?tan" type="text/css"
385 @ media="screen">
386 @ </head>
387 @ <body>
388 @ <div class="header">
389 @ <div class="title">$<title></div>
@@ -402,11 +402,11 @@
402 @ html "<a href=''$home$index_page''>Home</a>\n"
403 @ if {[anycap jor]} {
404 @ html "<a href=''$home/timeline''>Timeline</a>\n"
405 @ }
406 @ if {[hascap oh]} {
407 @ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
408 @ }
409 @ if {[hascap o]} {
410 @ html "<a href=''$home/brlist''>Branches</a>\n"
411 @ html "<a href=''$home/taglist''>Tags</a>\n"
412 @ }
@@ -620,17 +620,17 @@
620 @ <head>
621 @ <base href="$baseurl/$current_page" />
622 @ <title>$<project_name>: $<title></title>
623 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
624 @ href="$home/timeline.rss">
625 @ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
626 @ media="screen">
627 @ </head>
628 @ <body>
629 @ <div class="header">
630 @ <div class="logo">
631 @ <img src="$home/logo" alt="logo">
632 @ <br />$<project_name>
633 @ </div>
634 @ <div class="title">$<title></div>
635 @ <div class="status"><th1>
636 @ if {[info exists login]} {
@@ -645,11 +645,11 @@
645 @ html "<a href=''$home$index_page''>Home</a>\n"
646 @ if {[anycap jor]} {
647 @ html "<a href=''$home/timeline''>Timeline</a>\n"
648 @ }
649 @ if {[hascap oh]} {
650 @ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
651 @ }
652 @ if {[hascap o]} {
653 @ html "<a href=''$home/brlist''>Branches</a>\n"
654 @ html "<a href=''$home/taglist''>Tags</a>\n"
655 @ }
@@ -881,17 +881,17 @@
881 @ <head>
882 @ <base href="$baseurl/$current_page" />
883 @ <title>$<project_name>: $<title></title>
884 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
885 @ href="$home/timeline.rss">
886 @ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
887 @ media="screen">
888 @ </head>
889 @ <body>
890 @ <div class="header">
891 @ <div class="logo">
892 @ <img src="$home/logo" alt="logo">
893 @ <br />$<project_name>
894 @ </div>
895 @ <div class="title">$<title></div>
896 @ <div class="status"><th1>
897 @ if {[info exists login]} {
@@ -906,11 +906,11 @@
906 @ html "<a href=''$home$index_page''>Home</a>\n"
907 @ if {[anycap jor]} {
908 @ html "<a href=''$home/timeline''>Timeline</a>\n"
909 @ }
910 @ if {[hascap oh]} {
911 @ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
912 @ }
913 @ if {[hascap o]} {
914 @ html "<a href=''$home/brlist''>Branches</a>\n"
915 @ html "<a href=''$home/taglist''>Tags</a>\n"
916 @ }
@@ -1109,11 +1109,11 @@
1109 @ <head>
1110 @ <base href="$baseurl/$current_page" />
1111 @ <title>$<project_name>: $<title></title>
1112 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
1113 @ href="$home/timeline.rss" />
1114 @ <link rel="stylesheet" href="$home/style.css?enhanced" type="text/css"
1115 @ media="screen" />
1116 @ </head>
1117 @ <body>
1118 @ <div class="header">
1119 @ <div class="logo">
@@ -1176,11 +1176,11 @@
1176 @ return $logourl
1177 @ }
1178 @ set logourl [getLogoUrl $baseurl]
1179 @ </th1>
1180 @ <a href="$logourl">
1181 @ <img src="$baseurl/logo" border="0" alt="$project_name">
1182 @ </a>
1183 @ </div>
1184 @ <div class="title"><small>$<project_name></small><br />$<title></div>
1185 @ <div class="status"><th1>
1186 @ if {[info exists login]} {
@@ -1195,11 +1195,11 @@
1195 @ html "<a href=''$home$index_page''>Home</a>\n"
1196 @ if {[anycap jor]} {
1197 @ html "<a href=''$home/timeline''>Timeline</a>\n"
1198 @ }
1199 @ if {[hascap oh]} {
1200 @ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
1201 @ }
1202 @ if {[hascap o]} {
1203 @ html "<a href=''$home/brlist''>Branches</a>\n"
1204 @ html "<a href=''$home/taglist''>Tags</a>\n"
1205 @ }
1206
--- src/skins.c
+++ src/skins.c
@@ -171,11 +171,11 @@
171 @ <head>
172 @ <base href="$baseurl/$current_page" />
173 @ <title>$<project_name>: $<title></title>
174 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
175 @ href="$home/timeline.rss">
176 @ <link rel="stylesheet" href="$stylesheet_url" type="text/css"
177 @ media="screen">
178 @ </head>
179 @ <body>
180 @ <div class="header">
181 @ <div class="title"><small>$<project_name></small><br />$<title></div>
@@ -192,11 +192,11 @@
192 @ html "<a href=''$home$index_page''>Home</a>\n"
193 @ if {[anycap jor]} {
194 @ html "<a href=''$home/timeline''>Timeline</a>\n"
195 @ }
196 @ if {[hascap oh]} {
197 @ html "<a href=''$home/tree?ci=tip''>Files</a>\n"
198 @ }
199 @ if {[hascap o]} {
200 @ html "<a href=''$home/brlist''>Branches</a>\n"
201 @ html "<a href=''$home/taglist''>Tags</a>\n"
202 @ }
@@ -379,11 +379,11 @@
379 @ <head>
380 @ <base href="$baseurl/$current_page" />
381 @ <title>$<project_name>: $<title></title>
382 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
383 @ href="$home/timeline.rss">
384 @ <link rel="stylesheet" href="$stylesheet_url" type="text/css"
385 @ media="screen">
386 @ </head>
387 @ <body>
388 @ <div class="header">
389 @ <div class="title">$<title></div>
@@ -402,11 +402,11 @@
402 @ html "<a href=''$home$index_page''>Home</a>\n"
403 @ if {[anycap jor]} {
404 @ html "<a href=''$home/timeline''>Timeline</a>\n"
405 @ }
406 @ if {[hascap oh]} {
407 @ html "<a href=''$home/tree?ci=tip''>Files</a>\n"
408 @ }
409 @ if {[hascap o]} {
410 @ html "<a href=''$home/brlist''>Branches</a>\n"
411 @ html "<a href=''$home/taglist''>Tags</a>\n"
412 @ }
@@ -620,17 +620,17 @@
620 @ <head>
621 @ <base href="$baseurl/$current_page" />
622 @ <title>$<project_name>: $<title></title>
623 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
624 @ href="$home/timeline.rss">
625 @ <link rel="stylesheet" href="$stylesheet_url" type="text/css"
626 @ media="screen">
627 @ </head>
628 @ <body>
629 @ <div class="header">
630 @ <div class="logo">
631 @ <img src="$logo_image_url" alt="logo">
632 @ <br />$<project_name>
633 @ </div>
634 @ <div class="title">$<title></div>
635 @ <div class="status"><th1>
636 @ if {[info exists login]} {
@@ -645,11 +645,11 @@
645 @ html "<a href=''$home$index_page''>Home</a>\n"
646 @ if {[anycap jor]} {
647 @ html "<a href=''$home/timeline''>Timeline</a>\n"
648 @ }
649 @ if {[hascap oh]} {
650 @ html "<a href=''$home/tree?ci=tip''>Files</a>\n"
651 @ }
652 @ if {[hascap o]} {
653 @ html "<a href=''$home/brlist''>Branches</a>\n"
654 @ html "<a href=''$home/taglist''>Tags</a>\n"
655 @ }
@@ -881,17 +881,17 @@
881 @ <head>
882 @ <base href="$baseurl/$current_page" />
883 @ <title>$<project_name>: $<title></title>
884 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
885 @ href="$home/timeline.rss">
886 @ <link rel="stylesheet" href="$stylesheet_url" type="text/css"
887 @ media="screen">
888 @ </head>
889 @ <body>
890 @ <div class="header">
891 @ <div class="logo">
892 @ <img src="$logo_image_url" alt="logo">
893 @ <br />$<project_name>
894 @ </div>
895 @ <div class="title">$<title></div>
896 @ <div class="status"><th1>
897 @ if {[info exists login]} {
@@ -906,11 +906,11 @@
906 @ html "<a href=''$home$index_page''>Home</a>\n"
907 @ if {[anycap jor]} {
908 @ html "<a href=''$home/timeline''>Timeline</a>\n"
909 @ }
910 @ if {[hascap oh]} {
911 @ html "<a href=''$home/tree?ci=tip''>Files</a>\n"
912 @ }
913 @ if {[hascap o]} {
914 @ html "<a href=''$home/brlist''>Branches</a>\n"
915 @ html "<a href=''$home/taglist''>Tags</a>\n"
916 @ }
@@ -1109,11 +1109,11 @@
1109 @ <head>
1110 @ <base href="$baseurl/$current_page" />
1111 @ <title>$<project_name>: $<title></title>
1112 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
1113 @ href="$home/timeline.rss" />
1114 @ <link rel="stylesheet" href="$stylesheet_url" type="text/css"
1115 @ media="screen" />
1116 @ </head>
1117 @ <body>
1118 @ <div class="header">
1119 @ <div class="logo">
@@ -1176,11 +1176,11 @@
1176 @ return $logourl
1177 @ }
1178 @ set logourl [getLogoUrl $baseurl]
1179 @ </th1>
1180 @ <a href="$logourl">
1181 @ <img src="$logo_image_url" border="0" alt="$project_name">
1182 @ </a>
1183 @ </div>
1184 @ <div class="title"><small>$<project_name></small><br />$<title></div>
1185 @ <div class="status"><th1>
1186 @ if {[info exists login]} {
@@ -1195,11 +1195,11 @@
1195 @ html "<a href=''$home$index_page''>Home</a>\n"
1196 @ if {[anycap jor]} {
1197 @ html "<a href=''$home/timeline''>Timeline</a>\n"
1198 @ }
1199 @ if {[hascap oh]} {
1200 @ html "<a href=''$home/tree?ci=tip''>Files</a>\n"
1201 @ }
1202 @ if {[hascap o]} {
1203 @ html "<a href=''$home/brlist''>Branches</a>\n"
1204 @ html "<a href=''$home/taglist''>Tags</a>\n"
1205 @ }
1206
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -19,10 +19,11 @@
1919
** shell against the repository database. The command-line shell itself
2020
** is a copy of the "shell.c" code from SQLite. This file contains logic
2121
** to initialize the code in shell.c.
2222
*/
2323
#include "config.h"
24
+#if !defined(USE_SYSTEM_SQLITE) || !USE_SYSTEM_SQLITE
2425
#include "sqlcmd.h"
2526
#include <zlib.h>
2627
2728
/*
2829
** Implementation of the "content(X)" SQL function. Return the complete
@@ -154,5 +155,7 @@
154155
*/
155156
void fossil_open(const char **pzRepoName){
156157
sqlite3_auto_extension((void(*)(void))sqlcmd_autoinit);
157158
*pzRepoName = g.zRepositoryName;
158159
}
160
+
161
+#endif /* !USE_SYSTEM_SQLITE */
159162
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -19,10 +19,11 @@
19 ** shell against the repository database. The command-line shell itself
20 ** is a copy of the "shell.c" code from SQLite. This file contains logic
21 ** to initialize the code in shell.c.
22 */
23 #include "config.h"
 
24 #include "sqlcmd.h"
25 #include <zlib.h>
26
27 /*
28 ** Implementation of the "content(X)" SQL function. Return the complete
@@ -154,5 +155,7 @@
154 */
155 void fossil_open(const char **pzRepoName){
156 sqlite3_auto_extension((void(*)(void))sqlcmd_autoinit);
157 *pzRepoName = g.zRepositoryName;
158 }
 
 
159
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -19,10 +19,11 @@
19 ** shell against the repository database. The command-line shell itself
20 ** is a copy of the "shell.c" code from SQLite. This file contains logic
21 ** to initialize the code in shell.c.
22 */
23 #include "config.h"
24 #if !defined(USE_SYSTEM_SQLITE) || !USE_SYSTEM_SQLITE
25 #include "sqlcmd.h"
26 #include <zlib.h>
27
28 /*
29 ** Implementation of the "content(X)" SQL function. Return the complete
@@ -154,5 +155,7 @@
155 */
156 void fossil_open(const char **pzRepoName){
157 sqlite3_auto_extension((void(*)(void))sqlcmd_autoinit);
158 *pzRepoName = g.zRepositoryName;
159 }
160
161 #endif /* !USE_SYSTEM_SQLITE */
162
+167 -73
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -135,11 +135,11 @@
135135
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
136136
** [sqlite_version()] and [sqlite_source_id()].
137137
*/
138138
#define SQLITE_VERSION "3.8.3"
139139
#define SQLITE_VERSION_NUMBER 3008003
140
-#define SQLITE_SOURCE_ID "2013-12-23 11:33:32 25b8a1c9ba77df3b7c78cbce922cb593d661696d"
140
+#define SQLITE_SOURCE_ID "2014-01-04 15:17:04 4e725f53131d3584319c710c8710a068989543c6"
141141
142142
/*
143143
** CAPI3REF: Run-Time Library Version Numbers
144144
** KEYWORDS: sqlite3_version, sqlite3_sourceid
145145
**
@@ -2426,15 +2426,17 @@
24262426
** already uses the largest possible [ROWID]. The PRNG is also used for
24272427
** the build-in random() and randomblob() SQL functions. This interface allows
24282428
** applications to access the same PRNG for other purposes.
24292429
**
24302430
** ^A call to this routine stores N bytes of randomness into buffer P.
2431
+** ^If N is less than one, then P can be a NULL pointer.
24312432
**
2432
-** ^The first time this routine is invoked (either internally or by
2433
-** the application) the PRNG is seeded using randomness obtained
2434
-** from the xRandomness method of the default [sqlite3_vfs] object.
2435
-** ^On all subsequent invocations, the pseudo-randomness is generated
2433
+** ^If this routine has not been previously called or if the previous
2434
+** call had N less than one, then the PRNG is seeded using randomness
2435
+** obtained from the xRandomness method of the default [sqlite3_vfs] object.
2436
+** ^If the previous call to this routine had an N of 1 or more then
2437
+** the pseudo-randomness is generated
24362438
** internally and without recourse to the [sqlite3_vfs] xRandomness
24372439
** method.
24382440
*/
24392441
SQLITE_API void sqlite3_randomness(int N, void *P);
24402442
@@ -9278,10 +9280,11 @@
92789280
SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
92799281
SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
92809282
SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
92819283
SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr);
92829284
SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr);
9285
+SQLITE_PRIVATE void sqlite3VdbeDeleteLastOpcode(Vdbe*);
92839286
SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
92849287
SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse*, Index*);
92859288
SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int);
92869289
SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
92879290
SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*);
@@ -10371,11 +10374,11 @@
1037110374
*/
1037210375
#define SQLITE_QueryFlattener 0x0001 /* Query flattening */
1037310376
#define SQLITE_ColumnCache 0x0002 /* Column cache */
1037410377
#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */
1037510378
#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */
10376
-#define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */
10379
+/* not used 0x0010 // Was: SQLITE_IdxRealAsInt */
1037710380
#define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */
1037810381
#define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */
1037910382
#define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */
1038010383
#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */
1038110384
#define SQLITE_Transitive 0x0200 /* Transitive constraints */
@@ -11605,10 +11608,13 @@
1160511608
int nErr; /* Number of errors seen */
1160611609
int nTab; /* Number of previously allocated VDBE cursors */
1160711610
int nMem; /* Number of memory cells used so far */
1160811611
int nSet; /* Number of sets used so far */
1160911612
int nOnce; /* Number of OP_Once instructions so far */
11613
+ int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */
11614
+ int nLabel; /* Number of labels used */
11615
+ int *aLabel; /* Space to hold the labels */
1161011616
int ckBase; /* Base register of data during check constraints */
1161111617
int iPartIdxTab; /* Table corresponding to a partial index */
1161211618
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
1161311619
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
1161411620
struct yColCache {
@@ -12284,11 +12290,10 @@
1228412290
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
1228512291
SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*);
1228612292
SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*);
1228712293
SQLITE_PRIVATE void sqlite3PrngSaveState(void);
1228812294
SQLITE_PRIVATE void sqlite3PrngRestoreState(void);
12289
-SQLITE_PRIVATE void sqlite3PrngResetState(void);
1229012295
SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3*,int);
1229112296
SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse*, int);
1229212297
SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb);
1229312298
SQLITE_PRIVATE void sqlite3BeginTransaction(Parse*, int);
1229412299
SQLITE_PRIVATE void sqlite3CommitTransaction(Parse*);
@@ -13776,19 +13781,13 @@
1377613781
Op *aOp; /* Space to hold the virtual machine's program */
1377713782
Mem *aMem; /* The memory locations */
1377813783
Mem **apArg; /* Arguments to currently executing user function */
1377913784
Mem *aColName; /* Column names to return */
1378013785
Mem *pResultSet; /* Pointer to an array of results */
13781
-#ifdef SQLITE_DEBUG
1378213786
Parse *pParse; /* Parsing context used to create this Vdbe */
13783
-#endif
1378413787
int nMem; /* Number of memory locations currently allocated */
1378513788
int nOp; /* Number of instructions in the program */
13786
- int nOpAlloc; /* Number of slots allocated for aOp[] */
13787
- int nLabel; /* Number of labels used */
13788
- int *aLabel; /* Space to hold the labels */
13789
- u16 nResColumn; /* Number of columns in one row of the result set */
1379013789
int nCursor; /* Number of slots in apCsr[] */
1379113790
u32 magic; /* Magic number for sanity checking */
1379213791
char *zErrMsg; /* Error message written here */
1379313792
Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
1379413793
VdbeCursor **apCsr; /* One element of this array for each open cursor */
@@ -13797,10 +13796,11 @@
1379713796
ynVar nVar; /* Number of entries in aVar[] */
1379813797
ynVar nzVar; /* Number of entries in azVar[] */
1379913798
u32 cacheCtr; /* VdbeCursor row cache generation counter */
1380013799
int pc; /* The program counter */
1380113800
int rc; /* Value to return */
13801
+ u16 nResColumn; /* Number of columns in one row of the result set */
1380213802
u8 errorAction; /* Recovery action to do in case of an error */
1380313803
u8 minWriteFileFormat; /* Minimum file format for writable database files */
1380413804
bft explain:2; /* True if EXPLAIN present on SQL command */
1380513805
bft inVtabMethod:2; /* See comments above */
1380613806
bft changeCntOn:1; /* True to update the change-counter */
@@ -20901,10 +20901,16 @@
2090120901
2090220902
#if SQLITE_THREADSAFE
2090320903
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
2090420904
sqlite3_mutex_enter(mutex);
2090520905
#endif
20906
+
20907
+ if( N<=0 ){
20908
+ wsdPrng.isInit = 0;
20909
+ sqlite3_mutex_leave(mutex);
20910
+ return;
20911
+ }
2090620912
2090720913
/* Initialize the state of the random number generator once,
2090820914
** the first time this routine is called. The seed value does
2090920915
** not need to contain a lot of randomness since we are not
2091020916
** trying to do secure encryption or anything like that...
@@ -20929,19 +20935,20 @@
2092920935
wsdPrng.s[i] = t;
2093020936
}
2093120937
wsdPrng.isInit = 1;
2093220938
}
2093320939
20934
- while( N-- ){
20940
+ assert( N>0 );
20941
+ do{
2093520942
wsdPrng.i++;
2093620943
t = wsdPrng.s[wsdPrng.i];
2093720944
wsdPrng.j += t;
2093820945
wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
2093920946
wsdPrng.s[wsdPrng.j] = t;
2094020947
t += wsdPrng.s[wsdPrng.i];
2094120948
*(zBuf++) = wsdPrng.s[t];
20942
- }
20949
+ }while( --N );
2094320950
sqlite3_mutex_leave(mutex);
2094420951
}
2094520952
2094620953
#ifndef SQLITE_OMIT_BUILTIN_TEST
2094720954
/*
@@ -20966,13 +20973,10 @@
2096620973
&GLOBAL(struct sqlite3PrngType, sqlite3Prng),
2096720974
&GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng),
2096820975
sizeof(sqlite3Prng)
2096920976
);
2097020977
}
20971
-SQLITE_PRIVATE void sqlite3PrngResetState(void){
20972
- GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0;
20973
-}
2097420978
#endif /* SQLITE_OMIT_BUILTIN_TEST */
2097520979
2097620980
/************** End of random.c **********************************************/
2097720981
/************** Begin file utf.c *********************************************/
2097820982
/*
@@ -23505,10 +23509,16 @@
2350523509
** it is larger than the struct CrashFile defined in test6.c.
2350623510
*/
2350723511
char aPadding[32];
2350823512
#endif
2350923513
};
23514
+
23515
+/* This variable holds the process id (pid) from when the xRandomness()
23516
+** method was called. If xOpen() is called from a different process id,
23517
+** indicating that a fork() has occurred, the PRNG will be reset.
23518
+*/
23519
+static int randomnessPid = 0;
2351023520
2351123521
/*
2351223522
** Allowed values for the unixFile.ctrlFlags bitmask:
2351323523
*/
2351423524
#define UNIXFILE_EXCL 0x01 /* Connections from one process only */
@@ -29104,10 +29114,20 @@
2910429114
assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
2910529115
|| eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
2910629116
|| eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
2910729117
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
2910829118
);
29119
+
29120
+ /* Detect a pid change and reset the PRNG. There is a race condition
29121
+ ** here such that two or more threads all trying to open databases at
29122
+ ** the same instant might all reset the PRNG. But multiple resets
29123
+ ** are harmless.
29124
+ */
29125
+ if( randomnessPid!=getpid() ){
29126
+ randomnessPid = getpid();
29127
+ sqlite3_randomness(0,0);
29128
+ }
2910929129
2911029130
memset(p, 0, sizeof(unixFile));
2911129131
2911229132
if( eType==SQLITE_OPEN_MAIN_DB ){
2911329133
UnixUnusedFd *pUnused;
@@ -29492,22 +29512,22 @@
2949229512
** When testing, initializing zBuf[] to zero is all we do. That means
2949329513
** that we always use the same random number sequence. This makes the
2949429514
** tests repeatable.
2949529515
*/
2949629516
memset(zBuf, 0, nBuf);
29517
+ randomnessPid = getpid();
2949729518
#if !defined(SQLITE_TEST)
2949829519
{
29499
- int pid, fd, got;
29520
+ int fd, got;
2950029521
fd = robust_open("/dev/urandom", O_RDONLY, 0);
2950129522
if( fd<0 ){
2950229523
time_t t;
2950329524
time(&t);
2950429525
memcpy(zBuf, &t, sizeof(t));
29505
- pid = getpid();
29506
- memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid));
29507
- assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf );
29508
- nBuf = sizeof(t) + sizeof(pid);
29526
+ memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid));
29527
+ assert( sizeof(t)+sizeof(randomnessPid)<=(size_t)nBuf );
29528
+ nBuf = sizeof(t) + sizeof(randomnessPid);
2950929529
}else{
2951029530
do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR );
2951129531
robust_close(0, fd, __LINE__);
2951229532
}
2951329533
}
@@ -61200,13 +61220,14 @@
6120061220
}
6120161221
p->pNext = db->pVdbe;
6120261222
p->pPrev = 0;
6120361223
db->pVdbe = p;
6120461224
p->magic = VDBE_MAGIC_INIT;
61205
-#if SQLITE_DEBUG
6120661225
p->pParse = pParse;
61207
-#endif
61226
+ assert( pParse->aLabel==0 );
61227
+ assert( pParse->nLabel==0 );
61228
+ assert( pParse->nOpAlloc==0 );
6120861229
return p;
6120961230
}
6121061231
6121161232
/*
6121261233
** Remember the SQL string for a prepared statement.
@@ -61258,17 +61279,18 @@
6125861279
** If an out-of-memory error occurs while resizing the array, return
6125961280
** SQLITE_NOMEM. In this case Vdbe.aOp and Vdbe.nOpAlloc remain
6126061281
** unchanged (this is so that any opcodes already allocated can be
6126161282
** correctly deallocated along with the rest of the Vdbe).
6126261283
*/
61263
-static int growOpArray(Vdbe *p){
61284
+static int growOpArray(Vdbe *v){
6126461285
VdbeOp *pNew;
61286
+ Parse *p = v->pParse;
6126561287
int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op)));
61266
- pNew = sqlite3DbRealloc(p->db, p->aOp, nNew*sizeof(Op));
61288
+ pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op));
6126761289
if( pNew ){
6126861290
p->nOpAlloc = sqlite3DbMallocSize(p->db, pNew)/sizeof(Op);
61269
- p->aOp = pNew;
61291
+ v->aOp = pNew;
6127061292
}
6127161293
return (pNew ? SQLITE_OK : SQLITE_NOMEM);
6127261294
}
6127361295
6127461296
#ifdef SQLITE_DEBUG
@@ -61303,11 +61325,11 @@
6130361325
VdbeOp *pOp;
6130461326
6130561327
i = p->nOp;
6130661328
assert( p->magic==VDBE_MAGIC_INIT );
6130761329
assert( op>0 && op<0xff );
61308
- if( p->nOpAlloc<=i ){
61330
+ if( p->pParse->nOpAlloc<=i ){
6130961331
if( growOpArray(p) ){
6131061332
return 1;
6131161333
}
6131261334
}
6131361335
p->nOp++;
@@ -61414,13 +61436,14 @@
6141461436
** always negative and P2 values are suppose to be non-negative.
6141561437
** Hence, a negative P2 value is a label that has yet to be resolved.
6141661438
**
6141761439
** Zero is returned if a malloc() fails.
6141861440
*/
61419
-SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *p){
61441
+SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *v){
61442
+ Parse *p = v->pParse;
6142061443
int i = p->nLabel++;
61421
- assert( p->magic==VDBE_MAGIC_INIT );
61444
+ assert( v->magic==VDBE_MAGIC_INIT );
6142261445
if( (i & (i-1))==0 ){
6142361446
p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel,
6142461447
(i*2+1)*sizeof(p->aLabel[0]));
6142561448
}
6142661449
if( p->aLabel ){
@@ -61432,16 +61455,17 @@
6143261455
/*
6143361456
** Resolve label "x" to be the address of the next instruction to
6143461457
** be inserted. The parameter "x" must have been obtained from
6143561458
** a prior call to sqlite3VdbeMakeLabel().
6143661459
*/
61437
-SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *p, int x){
61460
+SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){
61461
+ Parse *p = v->pParse;
6143861462
int j = -1-x;
61439
- assert( p->magic==VDBE_MAGIC_INIT );
61463
+ assert( v->magic==VDBE_MAGIC_INIT );
6144061464
assert( j<p->nLabel );
6144161465
if( j>=0 && p->aLabel ){
61442
- p->aLabel[j] = p->nOp;
61466
+ p->aLabel[j] = v->nOp;
6144361467
}
6144461468
}
6144561469
6144661470
/*
6144761471
** Mark the VDBE as one that can only be run one time.
@@ -61586,11 +61610,12 @@
6158661610
*/
6158761611
static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
6158861612
int i;
6158961613
int nMaxArgs = *pMaxFuncArgs;
6159061614
Op *pOp;
61591
- int *aLabel = p->aLabel;
61615
+ Parse *pParse = p->pParse;
61616
+ int *aLabel = pParse->aLabel;
6159261617
p->readOnly = 1;
6159361618
p->bIsReader = 0;
6159461619
for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
6159561620
u8 opcode = pOp->opcode;
6159661621
@@ -61649,16 +61674,17 @@
6164961674
}
6165061675
}
6165161676
6165261677
pOp->opflags = sqlite3OpcodeProperty[opcode];
6165361678
if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){
61654
- assert( -1-pOp->p2<p->nLabel );
61679
+ assert( -1-pOp->p2<pParse->nLabel );
6165561680
pOp->p2 = aLabel[-1-pOp->p2];
6165661681
}
6165761682
}
61658
- sqlite3DbFree(p->db, p->aLabel);
61659
- p->aLabel = 0;
61683
+ sqlite3DbFree(p->db, pParse->aLabel);
61684
+ pParse->aLabel = 0;
61685
+ pParse->nLabel = 0;
6166061686
*pMaxFuncArgs = nMaxArgs;
6166161687
assert( p->bIsReader!=0 || p->btreeMask==0 );
6166261688
}
6166361689
6166461690
/*
@@ -61698,11 +61724,11 @@
6169861724
** address of the first operation added.
6169961725
*/
6170061726
SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
6170161727
int addr;
6170261728
assert( p->magic==VDBE_MAGIC_INIT );
61703
- if( p->nOp + nOp > p->nOpAlloc && growOpArray(p) ){
61729
+ if( p->nOp + nOp > p->pParse->nOpAlloc && growOpArray(p) ){
6170461730
return 0;
6170561731
}
6170661732
addr = p->nOp;
6170761733
if( ALWAYS(nOp>0) ){
6170861734
int i;
@@ -61886,10 +61912,17 @@
6188661912
memset(pOp, 0, sizeof(pOp[0]));
6188761913
pOp->opcode = OP_Noop;
6188861914
if( addr==p->nOp-1 ) p->nOp--;
6188961915
}
6189061916
}
61917
+
61918
+/*
61919
+** Remove the last opcode inserted
61920
+*/
61921
+SQLITE_PRIVATE void sqlite3VdbeDeleteLastOpcode(Vdbe *p){
61922
+ p->nOp--;
61923
+}
6189161924
6189261925
/*
6189361926
** Change the value of the P4 operand for a specific instruction.
6189461927
** This routine is useful when a large program is loaded from a
6189561928
** static array using sqlite3VdbeAddOpList but we want to make a
@@ -62768,10 +62801,11 @@
6276862801
6276962802
assert( p!=0 );
6277062803
assert( p->nOp>0 );
6277162804
assert( pParse!=0 );
6277262805
assert( p->magic==VDBE_MAGIC_INIT );
62806
+ assert( pParse==p->pParse );
6277362807
db = p->db;
6277462808
assert( db->mallocFailed==0 );
6277562809
nVar = pParse->nVar;
6277662810
nMem = pParse->nMem;
6277762811
nCursor = pParse->nTab;
@@ -62791,12 +62825,12 @@
6279162825
nMem += nCursor;
6279262826
6279362827
/* Allocate space for memory registers, SQL variables, VDBE cursors and
6279462828
** an array to marshal SQL function arguments in.
6279562829
*/
62796
- zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */
62797
- zEnd = (u8*)&p->aOp[p->nOpAlloc]; /* First byte past end of zCsr[] */
62830
+ zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */
62831
+ zEnd = (u8*)&p->aOp[pParse->nOpAlloc]; /* First byte past end of zCsr[] */
6279862832
6279962833
resolveP2Values(p, &nArg);
6280062834
p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort);
6280162835
if( pParse->explain && nMem<10 ){
6280262836
nMem = 10;
@@ -63795,11 +63829,10 @@
6379563829
vdbeFreeOpArray(db, pSub->aOp, pSub->nOp);
6379663830
sqlite3DbFree(db, pSub);
6379763831
}
6379863832
for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
6379963833
vdbeFreeOpArray(db, p->aOp, p->nOp);
63800
- sqlite3DbFree(db, p->aLabel);
6380163834
sqlite3DbFree(db, p->aColName);
6380263835
sqlite3DbFree(db, p->zSql);
6380363836
sqlite3DbFree(db, p->pFree);
6380463837
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
6380563838
sqlite3DbFree(db, p->zExplain);
@@ -76765,20 +76798,29 @@
7676576798
}
7676676799
return p;
7676776800
}
7676876801
7676976802
/*
76770
-** Return 1 if an expression must be FALSE in all cases and 0 if the
76771
-** expression might be true. This is an optimization. If is OK to
76772
-** return 0 here even if the expression really is always false (a
76773
-** false negative). But it is a bug to return 1 if the expression
76774
-** might be true in some rare circumstances (a false positive.)
76803
+** If the expression is always either TRUE or FALSE (respectively),
76804
+** then return 1. If one cannot determine the truth value of the
76805
+** expression at compile-time return 0.
76806
+**
76807
+** This is an optimization. If is OK to return 0 here even if
76808
+** the expression really is always false or false (a false negative).
76809
+** But it is a bug to return 1 if the expression might have different
76810
+** boolean values in different circumstances (a false positive.)
7677576811
**
7677676812
** Note that if the expression is part of conditional for a
7677776813
** LEFT JOIN, then we cannot determine at compile-time whether or not
7677876814
** is it true or false, so always return 0.
7677976815
*/
76816
+static int exprAlwaysTrue(Expr *p){
76817
+ int v = 0;
76818
+ if( ExprHasProperty(p, EP_FromJoin) ) return 0;
76819
+ if( !sqlite3ExprIsInteger(p, &v) ) return 0;
76820
+ return v!=0;
76821
+}
7678076822
static int exprAlwaysFalse(Expr *p){
7678176823
int v = 0;
7678276824
if( ExprHasProperty(p, EP_FromJoin) ) return 0;
7678376825
if( !sqlite3ExprIsInteger(p, &v) ) return 0;
7678476826
return v==0;
@@ -79621,11 +79663,11 @@
7962179663
sqlite3ExplainPush(pOut);
7962279664
for(i=0; i<pList->nExpr; i++){
7962379665
sqlite3ExplainPrintf(pOut, "item[%d] = ", i);
7962479666
sqlite3ExplainPush(pOut);
7962579667
sqlite3ExplainExpr(pOut, pList->a[i].pExpr);
79626
- sqlite3ExplainPop(pOut);
79668
+ sqlite3ExplainPop(pOut, 1);
7962779669
if( pList->a[i].zName ){
7962879670
sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName);
7962979671
}
7963079672
if( pList->a[i].bSpanIsTab ){
7963179673
sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan);
@@ -79771,21 +79813,23 @@
7977179813
op = pExpr->op;
7977279814
switch( op ){
7977379815
case TK_AND: {
7977479816
int d2 = sqlite3VdbeMakeLabel(v);
7977579817
testcase( jumpIfNull==0 );
79776
- sqlite3ExprCachePush(pParse);
7977779818
sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL);
79819
+ sqlite3ExprCachePush(pParse);
7977879820
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
7977979821
sqlite3VdbeResolveLabel(v, d2);
7978079822
sqlite3ExprCachePop(pParse, 1);
7978179823
break;
7978279824
}
7978379825
case TK_OR: {
7978479826
testcase( jumpIfNull==0 );
7978579827
sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
79828
+ sqlite3ExprCachePush(pParse);
7978679829
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
79830
+ sqlite3ExprCachePop(pParse, 1);
7978779831
break;
7978879832
}
7978979833
case TK_NOT: {
7979079834
testcase( jumpIfNull==0 );
7979179835
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
@@ -79856,14 +79900,20 @@
7985679900
sqlite3VdbeResolveLabel(v, destIfFalse);
7985779901
break;
7985879902
}
7985979903
#endif
7986079904
default: {
79861
- r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
79862
- sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0);
79863
- testcase( regFree1==0 );
79864
- testcase( jumpIfNull==0 );
79905
+ if( exprAlwaysTrue(pExpr) ){
79906
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, dest);
79907
+ }else if( exprAlwaysFalse(pExpr) ){
79908
+ /* No-op */
79909
+ }else{
79910
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
79911
+ sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0);
79912
+ testcase( regFree1==0 );
79913
+ testcase( jumpIfNull==0 );
79914
+ }
7986579915
break;
7986679916
}
7986779917
}
7986879918
sqlite3ReleaseTempReg(pParse, regFree1);
7986979919
sqlite3ReleaseTempReg(pParse, regFree2);
@@ -79922,18 +79972,20 @@
7992279972
7992379973
switch( pExpr->op ){
7992479974
case TK_AND: {
7992579975
testcase( jumpIfNull==0 );
7992679976
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
79977
+ sqlite3ExprCachePush(pParse);
7992779978
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
79979
+ sqlite3ExprCachePop(pParse, 1);
7992879980
break;
7992979981
}
7993079982
case TK_OR: {
7993179983
int d2 = sqlite3VdbeMakeLabel(v);
7993279984
testcase( jumpIfNull==0 );
79933
- sqlite3ExprCachePush(pParse);
7993479985
sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL);
79986
+ sqlite3ExprCachePush(pParse);
7993579987
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
7993679988
sqlite3VdbeResolveLabel(v, d2);
7993779989
sqlite3ExprCachePop(pParse, 1);
7993879990
break;
7993979991
}
@@ -80001,14 +80053,20 @@
8000180053
}
8000280054
break;
8000380055
}
8000480056
#endif
8000580057
default: {
80006
- r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
80007
- sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0);
80008
- testcase( regFree1==0 );
80009
- testcase( jumpIfNull==0 );
80058
+ if( exprAlwaysFalse(pExpr) ){
80059
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, dest);
80060
+ }else if( exprAlwaysTrue(pExpr) ){
80061
+ /* no-op */
80062
+ }else{
80063
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
80064
+ sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0);
80065
+ testcase( regFree1==0 );
80066
+ testcase( jumpIfNull==0 );
80067
+ }
8001080068
break;
8001180069
}
8001280070
}
8001380071
sqlite3ReleaseTempReg(pParse, regFree1);
8001480072
sqlite3ReleaseTempReg(pParse, regFree2);
@@ -89357,22 +89415,22 @@
8935789415
nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn;
8935889416
regBase = sqlite3GetTempRange(pParse, nCol);
8935989417
for(j=0; j<nCol; j++){
8936089418
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j],
8936189419
regBase+j);
89420
+ /* If the column affinity is REAL but the number is an integer, then it
89421
+ ** might be stored in the table as an integer (using a compact
89422
+ ** representation) then converted to REAL by an OP_RealAffinity opcode.
89423
+ ** But we are getting ready to store this value back into an index, where
89424
+ ** it should be converted by to INTEGER again. So omit the OP_RealAffinity
89425
+ ** opcode if it is present */
89426
+ if( sqlite3VdbeGetOp(v, -1)->opcode==OP_RealAffinity ){
89427
+ sqlite3VdbeDeleteLastOpcode(v);
89428
+ }
8936289429
}
8936389430
if( regOut ){
89364
- const char *zAff;
89365
- if( pTab->pSelect
89366
- || OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt)
89367
- ){
89368
- zAff = 0;
89369
- }else{
89370
- zAff = sqlite3IndexAffinityStr(v, pIdx);
89371
- }
8937289431
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut);
89373
- sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT);
8937489432
}
8937589433
sqlite3ReleaseTempRange(pParse, regBase, nCol);
8937689434
return regBase;
8937789435
}
8937889436
@@ -93721,10 +93779,11 @@
9372193779
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
9372293780
int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
9372393781
int ipkTop = 0; /* Top of the rowid change constraint check */
9372493782
int ipkBottom = 0; /* Bottom of the rowid change constraint check */
9372593783
u8 isUpdate; /* True if this is an UPDATE operation */
93784
+ int regRowid = -1; /* Register holding ROWID value */
9372693785
9372793786
isUpdate = regOldData!=0;
9372893787
db = pParse->db;
9372993788
v = sqlite3GetVdbe(pParse);
9373093789
assert( v!=0 );
@@ -93951,11 +94010,13 @@
9395194010
regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn);
9395294011
for(i=0; i<pIdx->nColumn; i++){
9395394012
int iField = pIdx->aiColumn[i];
9395494013
int x;
9395594014
if( iField<0 || iField==pTab->iPKey ){
94015
+ if( regRowid==regIdx+i ) continue; /* ROWID already in regIdx+i */
9395694016
x = regNewData;
94017
+ regRowid = pIdx->pPartIdxWhere ? -1 : regIdx+i;
9395794018
}else{
9395894019
x = iField + regNewData + 1;
9395994020
}
9396094021
sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i);
9396194022
VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName));
@@ -98780,11 +98841,15 @@
9878098841
9878198842
/*
9878298843
** Free all memory allocations in the pParse object
9878398844
*/
9878498845
SQLITE_PRIVATE void sqlite3ParserReset(Parse *pParse){
98785
- if( pParse ) sqlite3ExprListDelete(pParse->db, pParse->pConstExpr);
98846
+ if( pParse ){
98847
+ sqlite3 *db = pParse->db;
98848
+ sqlite3DbFree(db, pParse->aLabel);
98849
+ sqlite3ExprListDelete(db, pParse->pConstExpr);
98850
+ }
9878698851
}
9878798852
9878898853
/*
9878998854
** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
9879098855
*/
@@ -113528,13 +113593,16 @@
113528113593
sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
113529113594
113530113595
/* Special case: a WHERE clause that is constant. Evaluate the
113531113596
** expression and either jump over all of the code or fall thru.
113532113597
*/
113533
- if( pWhere && (nTabList==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){
113534
- sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, SQLITE_JUMPIFNULL);
113535
- pWhere = 0;
113598
+ for(ii=0; ii<sWLB.pWC->nTerm; ii++){
113599
+ if( nTabList==0 || sqlite3ExprIsConstantNotJoin(sWLB.pWC->a[ii].pExpr) ){
113600
+ sqlite3ExprIfFalse(pParse, sWLB.pWC->a[ii].pExpr, pWInfo->iBreak,
113601
+ SQLITE_JUMPIFNULL);
113602
+ sWLB.pWC->a[ii].wtFlags |= TERM_CODED;
113603
+ }
113536113604
}
113537113605
113538113606
/* Special case: No FROM clause
113539113607
*/
113540113608
if( nTabList==0 ){
@@ -121784,11 +121852,11 @@
121784121852
** Reset the PRNG back to its uninitialized state. The next call
121785121853
** to sqlite3_randomness() will reseed the PRNG using a single call
121786121854
** to the xRandomness method of the default VFS.
121787121855
*/
121788121856
case SQLITE_TESTCTRL_PRNG_RESET: {
121789
- sqlite3PrngResetState();
121857
+ sqlite3_randomness(0,0);
121790121858
break;
121791121859
}
121792121860
121793121861
/*
121794121862
** sqlite3_test_control(BITVEC_TEST, size, program)
@@ -124767,10 +124835,23 @@
124767124835
sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
124768124836
char **pzErr /* OUT: sqlite3_malloc'd error message */
124769124837
){
124770124838
return fts3InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr);
124771124839
}
124840
+
124841
+/*
124842
+** Set the pIdxInfo->estimatedRows variable to nRow. Unless this
124843
+** extension is currently being used by a version of SQLite too old to
124844
+** support estimatedRows. In that case this function is a no-op.
124845
+*/
124846
+static void setEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
124847
+#if SQLITE_VERSION_NUMBER>=3008002
124848
+ if( sqlite3_libversion_number()>=3008002 ){
124849
+ pIdxInfo->estimatedRows = nRow;
124850
+ }
124851
+#endif
124852
+}
124772124853
124773124854
/*
124774124855
** Implementation of the xBestIndex method for FTS3 tables. There
124775124856
** are three possible strategies, in order of preference:
124776124857
**
@@ -124795,11 +124876,24 @@
124795124876
pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
124796124877
pInfo->estimatedCost = 5000000;
124797124878
for(i=0; i<pInfo->nConstraint; i++){
124798124879
int bDocid; /* True if this constraint is on docid */
124799124880
struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i];
124800
- if( pCons->usable==0 ) continue;
124881
+ if( pCons->usable==0 ){
124882
+ if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
124883
+ /* There exists an unusable MATCH constraint. This means that if
124884
+ ** the planner does elect to use the results of this call as part
124885
+ ** of the overall query plan the user will see an "unable to use
124886
+ ** function MATCH in the requested context" error. To discourage
124887
+ ** this, return a very high cost here. */
124888
+ pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
124889
+ pInfo->estimatedCost = 1e50;
124890
+ setEstimatedRows(pInfo, ((sqlite3_int64)1) << 50);
124891
+ return SQLITE_OK;
124892
+ }
124893
+ continue;
124894
+ }
124801124895
124802124896
bDocid = (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1);
124803124897
124804124898
/* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */
124805124899
if( iCons<0 && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && bDocid ){
124806124900
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -135,11 +135,11 @@
135 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
136 ** [sqlite_version()] and [sqlite_source_id()].
137 */
138 #define SQLITE_VERSION "3.8.3"
139 #define SQLITE_VERSION_NUMBER 3008003
140 #define SQLITE_SOURCE_ID "2013-12-23 11:33:32 25b8a1c9ba77df3b7c78cbce922cb593d661696d"
141
142 /*
143 ** CAPI3REF: Run-Time Library Version Numbers
144 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
145 **
@@ -2426,15 +2426,17 @@
2426 ** already uses the largest possible [ROWID]. The PRNG is also used for
2427 ** the build-in random() and randomblob() SQL functions. This interface allows
2428 ** applications to access the same PRNG for other purposes.
2429 **
2430 ** ^A call to this routine stores N bytes of randomness into buffer P.
 
2431 **
2432 ** ^The first time this routine is invoked (either internally or by
2433 ** the application) the PRNG is seeded using randomness obtained
2434 ** from the xRandomness method of the default [sqlite3_vfs] object.
2435 ** ^On all subsequent invocations, the pseudo-randomness is generated
 
2436 ** internally and without recourse to the [sqlite3_vfs] xRandomness
2437 ** method.
2438 */
2439 SQLITE_API void sqlite3_randomness(int N, void *P);
2440
@@ -9278,10 +9280,11 @@
9278 SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
9279 SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
9280 SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
9281 SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr);
9282 SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr);
 
9283 SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
9284 SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse*, Index*);
9285 SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int);
9286 SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
9287 SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*);
@@ -10371,11 +10374,11 @@
10371 */
10372 #define SQLITE_QueryFlattener 0x0001 /* Query flattening */
10373 #define SQLITE_ColumnCache 0x0002 /* Column cache */
10374 #define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */
10375 #define SQLITE_FactorOutConst 0x0008 /* Constant factoring */
10376 #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */
10377 #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */
10378 #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */
10379 #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */
10380 #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */
10381 #define SQLITE_Transitive 0x0200 /* Transitive constraints */
@@ -11605,10 +11608,13 @@
11605 int nErr; /* Number of errors seen */
11606 int nTab; /* Number of previously allocated VDBE cursors */
11607 int nMem; /* Number of memory cells used so far */
11608 int nSet; /* Number of sets used so far */
11609 int nOnce; /* Number of OP_Once instructions so far */
 
 
 
11610 int ckBase; /* Base register of data during check constraints */
11611 int iPartIdxTab; /* Table corresponding to a partial index */
11612 int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
11613 int iCacheCnt; /* Counter used to generate aColCache[].lru values */
11614 struct yColCache {
@@ -12284,11 +12290,10 @@
12284 SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
12285 SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*);
12286 SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*);
12287 SQLITE_PRIVATE void sqlite3PrngSaveState(void);
12288 SQLITE_PRIVATE void sqlite3PrngRestoreState(void);
12289 SQLITE_PRIVATE void sqlite3PrngResetState(void);
12290 SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3*,int);
12291 SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse*, int);
12292 SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb);
12293 SQLITE_PRIVATE void sqlite3BeginTransaction(Parse*, int);
12294 SQLITE_PRIVATE void sqlite3CommitTransaction(Parse*);
@@ -13776,19 +13781,13 @@
13776 Op *aOp; /* Space to hold the virtual machine's program */
13777 Mem *aMem; /* The memory locations */
13778 Mem **apArg; /* Arguments to currently executing user function */
13779 Mem *aColName; /* Column names to return */
13780 Mem *pResultSet; /* Pointer to an array of results */
13781 #ifdef SQLITE_DEBUG
13782 Parse *pParse; /* Parsing context used to create this Vdbe */
13783 #endif
13784 int nMem; /* Number of memory locations currently allocated */
13785 int nOp; /* Number of instructions in the program */
13786 int nOpAlloc; /* Number of slots allocated for aOp[] */
13787 int nLabel; /* Number of labels used */
13788 int *aLabel; /* Space to hold the labels */
13789 u16 nResColumn; /* Number of columns in one row of the result set */
13790 int nCursor; /* Number of slots in apCsr[] */
13791 u32 magic; /* Magic number for sanity checking */
13792 char *zErrMsg; /* Error message written here */
13793 Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
13794 VdbeCursor **apCsr; /* One element of this array for each open cursor */
@@ -13797,10 +13796,11 @@
13797 ynVar nVar; /* Number of entries in aVar[] */
13798 ynVar nzVar; /* Number of entries in azVar[] */
13799 u32 cacheCtr; /* VdbeCursor row cache generation counter */
13800 int pc; /* The program counter */
13801 int rc; /* Value to return */
 
13802 u8 errorAction; /* Recovery action to do in case of an error */
13803 u8 minWriteFileFormat; /* Minimum file format for writable database files */
13804 bft explain:2; /* True if EXPLAIN present on SQL command */
13805 bft inVtabMethod:2; /* See comments above */
13806 bft changeCntOn:1; /* True to update the change-counter */
@@ -20901,10 +20901,16 @@
20901
20902 #if SQLITE_THREADSAFE
20903 sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
20904 sqlite3_mutex_enter(mutex);
20905 #endif
 
 
 
 
 
 
20906
20907 /* Initialize the state of the random number generator once,
20908 ** the first time this routine is called. The seed value does
20909 ** not need to contain a lot of randomness since we are not
20910 ** trying to do secure encryption or anything like that...
@@ -20929,19 +20935,20 @@
20929 wsdPrng.s[i] = t;
20930 }
20931 wsdPrng.isInit = 1;
20932 }
20933
20934 while( N-- ){
 
20935 wsdPrng.i++;
20936 t = wsdPrng.s[wsdPrng.i];
20937 wsdPrng.j += t;
20938 wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
20939 wsdPrng.s[wsdPrng.j] = t;
20940 t += wsdPrng.s[wsdPrng.i];
20941 *(zBuf++) = wsdPrng.s[t];
20942 }
20943 sqlite3_mutex_leave(mutex);
20944 }
20945
20946 #ifndef SQLITE_OMIT_BUILTIN_TEST
20947 /*
@@ -20966,13 +20973,10 @@
20966 &GLOBAL(struct sqlite3PrngType, sqlite3Prng),
20967 &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng),
20968 sizeof(sqlite3Prng)
20969 );
20970 }
20971 SQLITE_PRIVATE void sqlite3PrngResetState(void){
20972 GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0;
20973 }
20974 #endif /* SQLITE_OMIT_BUILTIN_TEST */
20975
20976 /************** End of random.c **********************************************/
20977 /************** Begin file utf.c *********************************************/
20978 /*
@@ -23505,10 +23509,16 @@
23505 ** it is larger than the struct CrashFile defined in test6.c.
23506 */
23507 char aPadding[32];
23508 #endif
23509 };
 
 
 
 
 
 
23510
23511 /*
23512 ** Allowed values for the unixFile.ctrlFlags bitmask:
23513 */
23514 #define UNIXFILE_EXCL 0x01 /* Connections from one process only */
@@ -29104,10 +29114,20 @@
29104 assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
29105 || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
29106 || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
29107 || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
29108 );
 
 
 
 
 
 
 
 
 
 
29109
29110 memset(p, 0, sizeof(unixFile));
29111
29112 if( eType==SQLITE_OPEN_MAIN_DB ){
29113 UnixUnusedFd *pUnused;
@@ -29492,22 +29512,22 @@
29492 ** When testing, initializing zBuf[] to zero is all we do. That means
29493 ** that we always use the same random number sequence. This makes the
29494 ** tests repeatable.
29495 */
29496 memset(zBuf, 0, nBuf);
 
29497 #if !defined(SQLITE_TEST)
29498 {
29499 int pid, fd, got;
29500 fd = robust_open("/dev/urandom", O_RDONLY, 0);
29501 if( fd<0 ){
29502 time_t t;
29503 time(&t);
29504 memcpy(zBuf, &t, sizeof(t));
29505 pid = getpid();
29506 memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid));
29507 assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf );
29508 nBuf = sizeof(t) + sizeof(pid);
29509 }else{
29510 do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR );
29511 robust_close(0, fd, __LINE__);
29512 }
29513 }
@@ -61200,13 +61220,14 @@
61200 }
61201 p->pNext = db->pVdbe;
61202 p->pPrev = 0;
61203 db->pVdbe = p;
61204 p->magic = VDBE_MAGIC_INIT;
61205 #if SQLITE_DEBUG
61206 p->pParse = pParse;
61207 #endif
 
 
61208 return p;
61209 }
61210
61211 /*
61212 ** Remember the SQL string for a prepared statement.
@@ -61258,17 +61279,18 @@
61258 ** If an out-of-memory error occurs while resizing the array, return
61259 ** SQLITE_NOMEM. In this case Vdbe.aOp and Vdbe.nOpAlloc remain
61260 ** unchanged (this is so that any opcodes already allocated can be
61261 ** correctly deallocated along with the rest of the Vdbe).
61262 */
61263 static int growOpArray(Vdbe *p){
61264 VdbeOp *pNew;
 
61265 int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op)));
61266 pNew = sqlite3DbRealloc(p->db, p->aOp, nNew*sizeof(Op));
61267 if( pNew ){
61268 p->nOpAlloc = sqlite3DbMallocSize(p->db, pNew)/sizeof(Op);
61269 p->aOp = pNew;
61270 }
61271 return (pNew ? SQLITE_OK : SQLITE_NOMEM);
61272 }
61273
61274 #ifdef SQLITE_DEBUG
@@ -61303,11 +61325,11 @@
61303 VdbeOp *pOp;
61304
61305 i = p->nOp;
61306 assert( p->magic==VDBE_MAGIC_INIT );
61307 assert( op>0 && op<0xff );
61308 if( p->nOpAlloc<=i ){
61309 if( growOpArray(p) ){
61310 return 1;
61311 }
61312 }
61313 p->nOp++;
@@ -61414,13 +61436,14 @@
61414 ** always negative and P2 values are suppose to be non-negative.
61415 ** Hence, a negative P2 value is a label that has yet to be resolved.
61416 **
61417 ** Zero is returned if a malloc() fails.
61418 */
61419 SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *p){
 
61420 int i = p->nLabel++;
61421 assert( p->magic==VDBE_MAGIC_INIT );
61422 if( (i & (i-1))==0 ){
61423 p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel,
61424 (i*2+1)*sizeof(p->aLabel[0]));
61425 }
61426 if( p->aLabel ){
@@ -61432,16 +61455,17 @@
61432 /*
61433 ** Resolve label "x" to be the address of the next instruction to
61434 ** be inserted. The parameter "x" must have been obtained from
61435 ** a prior call to sqlite3VdbeMakeLabel().
61436 */
61437 SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *p, int x){
 
61438 int j = -1-x;
61439 assert( p->magic==VDBE_MAGIC_INIT );
61440 assert( j<p->nLabel );
61441 if( j>=0 && p->aLabel ){
61442 p->aLabel[j] = p->nOp;
61443 }
61444 }
61445
61446 /*
61447 ** Mark the VDBE as one that can only be run one time.
@@ -61586,11 +61610,12 @@
61586 */
61587 static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
61588 int i;
61589 int nMaxArgs = *pMaxFuncArgs;
61590 Op *pOp;
61591 int *aLabel = p->aLabel;
 
61592 p->readOnly = 1;
61593 p->bIsReader = 0;
61594 for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
61595 u8 opcode = pOp->opcode;
61596
@@ -61649,16 +61674,17 @@
61649 }
61650 }
61651
61652 pOp->opflags = sqlite3OpcodeProperty[opcode];
61653 if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){
61654 assert( -1-pOp->p2<p->nLabel );
61655 pOp->p2 = aLabel[-1-pOp->p2];
61656 }
61657 }
61658 sqlite3DbFree(p->db, p->aLabel);
61659 p->aLabel = 0;
 
61660 *pMaxFuncArgs = nMaxArgs;
61661 assert( p->bIsReader!=0 || p->btreeMask==0 );
61662 }
61663
61664 /*
@@ -61698,11 +61724,11 @@
61698 ** address of the first operation added.
61699 */
61700 SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
61701 int addr;
61702 assert( p->magic==VDBE_MAGIC_INIT );
61703 if( p->nOp + nOp > p->nOpAlloc && growOpArray(p) ){
61704 return 0;
61705 }
61706 addr = p->nOp;
61707 if( ALWAYS(nOp>0) ){
61708 int i;
@@ -61886,10 +61912,17 @@
61886 memset(pOp, 0, sizeof(pOp[0]));
61887 pOp->opcode = OP_Noop;
61888 if( addr==p->nOp-1 ) p->nOp--;
61889 }
61890 }
 
 
 
 
 
 
 
61891
61892 /*
61893 ** Change the value of the P4 operand for a specific instruction.
61894 ** This routine is useful when a large program is loaded from a
61895 ** static array using sqlite3VdbeAddOpList but we want to make a
@@ -62768,10 +62801,11 @@
62768
62769 assert( p!=0 );
62770 assert( p->nOp>0 );
62771 assert( pParse!=0 );
62772 assert( p->magic==VDBE_MAGIC_INIT );
 
62773 db = p->db;
62774 assert( db->mallocFailed==0 );
62775 nVar = pParse->nVar;
62776 nMem = pParse->nMem;
62777 nCursor = pParse->nTab;
@@ -62791,12 +62825,12 @@
62791 nMem += nCursor;
62792
62793 /* Allocate space for memory registers, SQL variables, VDBE cursors and
62794 ** an array to marshal SQL function arguments in.
62795 */
62796 zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */
62797 zEnd = (u8*)&p->aOp[p->nOpAlloc]; /* First byte past end of zCsr[] */
62798
62799 resolveP2Values(p, &nArg);
62800 p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort);
62801 if( pParse->explain && nMem<10 ){
62802 nMem = 10;
@@ -63795,11 +63829,10 @@
63795 vdbeFreeOpArray(db, pSub->aOp, pSub->nOp);
63796 sqlite3DbFree(db, pSub);
63797 }
63798 for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
63799 vdbeFreeOpArray(db, p->aOp, p->nOp);
63800 sqlite3DbFree(db, p->aLabel);
63801 sqlite3DbFree(db, p->aColName);
63802 sqlite3DbFree(db, p->zSql);
63803 sqlite3DbFree(db, p->pFree);
63804 #if defined(SQLITE_ENABLE_TREE_EXPLAIN)
63805 sqlite3DbFree(db, p->zExplain);
@@ -76765,20 +76798,29 @@
76765 }
76766 return p;
76767 }
76768
76769 /*
76770 ** Return 1 if an expression must be FALSE in all cases and 0 if the
76771 ** expression might be true. This is an optimization. If is OK to
76772 ** return 0 here even if the expression really is always false (a
76773 ** false negative). But it is a bug to return 1 if the expression
76774 ** might be true in some rare circumstances (a false positive.)
 
 
 
76775 **
76776 ** Note that if the expression is part of conditional for a
76777 ** LEFT JOIN, then we cannot determine at compile-time whether or not
76778 ** is it true or false, so always return 0.
76779 */
 
 
 
 
 
 
76780 static int exprAlwaysFalse(Expr *p){
76781 int v = 0;
76782 if( ExprHasProperty(p, EP_FromJoin) ) return 0;
76783 if( !sqlite3ExprIsInteger(p, &v) ) return 0;
76784 return v==0;
@@ -79621,11 +79663,11 @@
79621 sqlite3ExplainPush(pOut);
79622 for(i=0; i<pList->nExpr; i++){
79623 sqlite3ExplainPrintf(pOut, "item[%d] = ", i);
79624 sqlite3ExplainPush(pOut);
79625 sqlite3ExplainExpr(pOut, pList->a[i].pExpr);
79626 sqlite3ExplainPop(pOut);
79627 if( pList->a[i].zName ){
79628 sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName);
79629 }
79630 if( pList->a[i].bSpanIsTab ){
79631 sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan);
@@ -79771,21 +79813,23 @@
79771 op = pExpr->op;
79772 switch( op ){
79773 case TK_AND: {
79774 int d2 = sqlite3VdbeMakeLabel(v);
79775 testcase( jumpIfNull==0 );
79776 sqlite3ExprCachePush(pParse);
79777 sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL);
 
79778 sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
79779 sqlite3VdbeResolveLabel(v, d2);
79780 sqlite3ExprCachePop(pParse, 1);
79781 break;
79782 }
79783 case TK_OR: {
79784 testcase( jumpIfNull==0 );
79785 sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
 
79786 sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
 
79787 break;
79788 }
79789 case TK_NOT: {
79790 testcase( jumpIfNull==0 );
79791 sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
@@ -79856,14 +79900,20 @@
79856 sqlite3VdbeResolveLabel(v, destIfFalse);
79857 break;
79858 }
79859 #endif
79860 default: {
79861 r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
79862 sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0);
79863 testcase( regFree1==0 );
79864 testcase( jumpIfNull==0 );
 
 
 
 
 
 
79865 break;
79866 }
79867 }
79868 sqlite3ReleaseTempReg(pParse, regFree1);
79869 sqlite3ReleaseTempReg(pParse, regFree2);
@@ -79922,18 +79972,20 @@
79922
79923 switch( pExpr->op ){
79924 case TK_AND: {
79925 testcase( jumpIfNull==0 );
79926 sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
 
79927 sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
 
79928 break;
79929 }
79930 case TK_OR: {
79931 int d2 = sqlite3VdbeMakeLabel(v);
79932 testcase( jumpIfNull==0 );
79933 sqlite3ExprCachePush(pParse);
79934 sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL);
 
79935 sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
79936 sqlite3VdbeResolveLabel(v, d2);
79937 sqlite3ExprCachePop(pParse, 1);
79938 break;
79939 }
@@ -80001,14 +80053,20 @@
80001 }
80002 break;
80003 }
80004 #endif
80005 default: {
80006 r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
80007 sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0);
80008 testcase( regFree1==0 );
80009 testcase( jumpIfNull==0 );
 
 
 
 
 
 
80010 break;
80011 }
80012 }
80013 sqlite3ReleaseTempReg(pParse, regFree1);
80014 sqlite3ReleaseTempReg(pParse, regFree2);
@@ -89357,22 +89415,22 @@
89357 nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn;
89358 regBase = sqlite3GetTempRange(pParse, nCol);
89359 for(j=0; j<nCol; j++){
89360 sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j],
89361 regBase+j);
 
 
 
 
 
 
 
 
 
89362 }
89363 if( regOut ){
89364 const char *zAff;
89365 if( pTab->pSelect
89366 || OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt)
89367 ){
89368 zAff = 0;
89369 }else{
89370 zAff = sqlite3IndexAffinityStr(v, pIdx);
89371 }
89372 sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut);
89373 sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT);
89374 }
89375 sqlite3ReleaseTempRange(pParse, regBase, nCol);
89376 return regBase;
89377 }
89378
@@ -93721,10 +93779,11 @@
93721 int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
93722 int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
93723 int ipkTop = 0; /* Top of the rowid change constraint check */
93724 int ipkBottom = 0; /* Bottom of the rowid change constraint check */
93725 u8 isUpdate; /* True if this is an UPDATE operation */
 
93726
93727 isUpdate = regOldData!=0;
93728 db = pParse->db;
93729 v = sqlite3GetVdbe(pParse);
93730 assert( v!=0 );
@@ -93951,11 +94010,13 @@
93951 regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn);
93952 for(i=0; i<pIdx->nColumn; i++){
93953 int iField = pIdx->aiColumn[i];
93954 int x;
93955 if( iField<0 || iField==pTab->iPKey ){
 
93956 x = regNewData;
 
93957 }else{
93958 x = iField + regNewData + 1;
93959 }
93960 sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i);
93961 VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName));
@@ -98780,11 +98841,15 @@
98780
98781 /*
98782 ** Free all memory allocations in the pParse object
98783 */
98784 SQLITE_PRIVATE void sqlite3ParserReset(Parse *pParse){
98785 if( pParse ) sqlite3ExprListDelete(pParse->db, pParse->pConstExpr);
 
 
 
 
98786 }
98787
98788 /*
98789 ** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
98790 */
@@ -113528,13 +113593,16 @@
113528 sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
113529
113530 /* Special case: a WHERE clause that is constant. Evaluate the
113531 ** expression and either jump over all of the code or fall thru.
113532 */
113533 if( pWhere && (nTabList==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){
113534 sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, SQLITE_JUMPIFNULL);
113535 pWhere = 0;
 
 
 
113536 }
113537
113538 /* Special case: No FROM clause
113539 */
113540 if( nTabList==0 ){
@@ -121784,11 +121852,11 @@
121784 ** Reset the PRNG back to its uninitialized state. The next call
121785 ** to sqlite3_randomness() will reseed the PRNG using a single call
121786 ** to the xRandomness method of the default VFS.
121787 */
121788 case SQLITE_TESTCTRL_PRNG_RESET: {
121789 sqlite3PrngResetState();
121790 break;
121791 }
121792
121793 /*
121794 ** sqlite3_test_control(BITVEC_TEST, size, program)
@@ -124767,10 +124835,23 @@
124767 sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
124768 char **pzErr /* OUT: sqlite3_malloc'd error message */
124769 ){
124770 return fts3InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr);
124771 }
 
 
 
 
 
 
 
 
 
 
 
 
 
124772
124773 /*
124774 ** Implementation of the xBestIndex method for FTS3 tables. There
124775 ** are three possible strategies, in order of preference:
124776 **
@@ -124795,11 +124876,24 @@
124795 pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
124796 pInfo->estimatedCost = 5000000;
124797 for(i=0; i<pInfo->nConstraint; i++){
124798 int bDocid; /* True if this constraint is on docid */
124799 struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i];
124800 if( pCons->usable==0 ) continue;
 
 
 
 
 
 
 
 
 
 
 
 
 
124801
124802 bDocid = (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1);
124803
124804 /* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */
124805 if( iCons<0 && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && bDocid ){
124806
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -135,11 +135,11 @@
135 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
136 ** [sqlite_version()] and [sqlite_source_id()].
137 */
138 #define SQLITE_VERSION "3.8.3"
139 #define SQLITE_VERSION_NUMBER 3008003
140 #define SQLITE_SOURCE_ID "2014-01-04 15:17:04 4e725f53131d3584319c710c8710a068989543c6"
141
142 /*
143 ** CAPI3REF: Run-Time Library Version Numbers
144 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
145 **
@@ -2426,15 +2426,17 @@
2426 ** already uses the largest possible [ROWID]. The PRNG is also used for
2427 ** the build-in random() and randomblob() SQL functions. This interface allows
2428 ** applications to access the same PRNG for other purposes.
2429 **
2430 ** ^A call to this routine stores N bytes of randomness into buffer P.
2431 ** ^If N is less than one, then P can be a NULL pointer.
2432 **
2433 ** ^If this routine has not been previously called or if the previous
2434 ** call had N less than one, then the PRNG is seeded using randomness
2435 ** obtained from the xRandomness method of the default [sqlite3_vfs] object.
2436 ** ^If the previous call to this routine had an N of 1 or more then
2437 ** the pseudo-randomness is generated
2438 ** internally and without recourse to the [sqlite3_vfs] xRandomness
2439 ** method.
2440 */
2441 SQLITE_API void sqlite3_randomness(int N, void *P);
2442
@@ -9278,10 +9280,11 @@
9280 SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
9281 SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
9282 SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
9283 SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr);
9284 SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr);
9285 SQLITE_PRIVATE void sqlite3VdbeDeleteLastOpcode(Vdbe*);
9286 SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
9287 SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse*, Index*);
9288 SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int);
9289 SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
9290 SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*);
@@ -10371,11 +10374,11 @@
10374 */
10375 #define SQLITE_QueryFlattener 0x0001 /* Query flattening */
10376 #define SQLITE_ColumnCache 0x0002 /* Column cache */
10377 #define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */
10378 #define SQLITE_FactorOutConst 0x0008 /* Constant factoring */
10379 /* not used 0x0010 // Was: SQLITE_IdxRealAsInt */
10380 #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */
10381 #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */
10382 #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */
10383 #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */
10384 #define SQLITE_Transitive 0x0200 /* Transitive constraints */
@@ -11605,10 +11608,13 @@
11608 int nErr; /* Number of errors seen */
11609 int nTab; /* Number of previously allocated VDBE cursors */
11610 int nMem; /* Number of memory cells used so far */
11611 int nSet; /* Number of sets used so far */
11612 int nOnce; /* Number of OP_Once instructions so far */
11613 int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */
11614 int nLabel; /* Number of labels used */
11615 int *aLabel; /* Space to hold the labels */
11616 int ckBase; /* Base register of data during check constraints */
11617 int iPartIdxTab; /* Table corresponding to a partial index */
11618 int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
11619 int iCacheCnt; /* Counter used to generate aColCache[].lru values */
11620 struct yColCache {
@@ -12284,11 +12290,10 @@
12290 SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
12291 SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*);
12292 SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*);
12293 SQLITE_PRIVATE void sqlite3PrngSaveState(void);
12294 SQLITE_PRIVATE void sqlite3PrngRestoreState(void);
 
12295 SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3*,int);
12296 SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse*, int);
12297 SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb);
12298 SQLITE_PRIVATE void sqlite3BeginTransaction(Parse*, int);
12299 SQLITE_PRIVATE void sqlite3CommitTransaction(Parse*);
@@ -13776,19 +13781,13 @@
13781 Op *aOp; /* Space to hold the virtual machine's program */
13782 Mem *aMem; /* The memory locations */
13783 Mem **apArg; /* Arguments to currently executing user function */
13784 Mem *aColName; /* Column names to return */
13785 Mem *pResultSet; /* Pointer to an array of results */
 
13786 Parse *pParse; /* Parsing context used to create this Vdbe */
 
13787 int nMem; /* Number of memory locations currently allocated */
13788 int nOp; /* Number of instructions in the program */
 
 
 
 
13789 int nCursor; /* Number of slots in apCsr[] */
13790 u32 magic; /* Magic number for sanity checking */
13791 char *zErrMsg; /* Error message written here */
13792 Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
13793 VdbeCursor **apCsr; /* One element of this array for each open cursor */
@@ -13797,10 +13796,11 @@
13796 ynVar nVar; /* Number of entries in aVar[] */
13797 ynVar nzVar; /* Number of entries in azVar[] */
13798 u32 cacheCtr; /* VdbeCursor row cache generation counter */
13799 int pc; /* The program counter */
13800 int rc; /* Value to return */
13801 u16 nResColumn; /* Number of columns in one row of the result set */
13802 u8 errorAction; /* Recovery action to do in case of an error */
13803 u8 minWriteFileFormat; /* Minimum file format for writable database files */
13804 bft explain:2; /* True if EXPLAIN present on SQL command */
13805 bft inVtabMethod:2; /* See comments above */
13806 bft changeCntOn:1; /* True to update the change-counter */
@@ -20901,10 +20901,16 @@
20901
20902 #if SQLITE_THREADSAFE
20903 sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
20904 sqlite3_mutex_enter(mutex);
20905 #endif
20906
20907 if( N<=0 ){
20908 wsdPrng.isInit = 0;
20909 sqlite3_mutex_leave(mutex);
20910 return;
20911 }
20912
20913 /* Initialize the state of the random number generator once,
20914 ** the first time this routine is called. The seed value does
20915 ** not need to contain a lot of randomness since we are not
20916 ** trying to do secure encryption or anything like that...
@@ -20929,19 +20935,20 @@
20935 wsdPrng.s[i] = t;
20936 }
20937 wsdPrng.isInit = 1;
20938 }
20939
20940 assert( N>0 );
20941 do{
20942 wsdPrng.i++;
20943 t = wsdPrng.s[wsdPrng.i];
20944 wsdPrng.j += t;
20945 wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
20946 wsdPrng.s[wsdPrng.j] = t;
20947 t += wsdPrng.s[wsdPrng.i];
20948 *(zBuf++) = wsdPrng.s[t];
20949 }while( --N );
20950 sqlite3_mutex_leave(mutex);
20951 }
20952
20953 #ifndef SQLITE_OMIT_BUILTIN_TEST
20954 /*
@@ -20966,13 +20973,10 @@
20973 &GLOBAL(struct sqlite3PrngType, sqlite3Prng),
20974 &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng),
20975 sizeof(sqlite3Prng)
20976 );
20977 }
 
 
 
20978 #endif /* SQLITE_OMIT_BUILTIN_TEST */
20979
20980 /************** End of random.c **********************************************/
20981 /************** Begin file utf.c *********************************************/
20982 /*
@@ -23505,10 +23509,16 @@
23509 ** it is larger than the struct CrashFile defined in test6.c.
23510 */
23511 char aPadding[32];
23512 #endif
23513 };
23514
23515 /* This variable holds the process id (pid) from when the xRandomness()
23516 ** method was called. If xOpen() is called from a different process id,
23517 ** indicating that a fork() has occurred, the PRNG will be reset.
23518 */
23519 static int randomnessPid = 0;
23520
23521 /*
23522 ** Allowed values for the unixFile.ctrlFlags bitmask:
23523 */
23524 #define UNIXFILE_EXCL 0x01 /* Connections from one process only */
@@ -29104,10 +29114,20 @@
29114 assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
29115 || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
29116 || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
29117 || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
29118 );
29119
29120 /* Detect a pid change and reset the PRNG. There is a race condition
29121 ** here such that two or more threads all trying to open databases at
29122 ** the same instant might all reset the PRNG. But multiple resets
29123 ** are harmless.
29124 */
29125 if( randomnessPid!=getpid() ){
29126 randomnessPid = getpid();
29127 sqlite3_randomness(0,0);
29128 }
29129
29130 memset(p, 0, sizeof(unixFile));
29131
29132 if( eType==SQLITE_OPEN_MAIN_DB ){
29133 UnixUnusedFd *pUnused;
@@ -29492,22 +29512,22 @@
29512 ** When testing, initializing zBuf[] to zero is all we do. That means
29513 ** that we always use the same random number sequence. This makes the
29514 ** tests repeatable.
29515 */
29516 memset(zBuf, 0, nBuf);
29517 randomnessPid = getpid();
29518 #if !defined(SQLITE_TEST)
29519 {
29520 int fd, got;
29521 fd = robust_open("/dev/urandom", O_RDONLY, 0);
29522 if( fd<0 ){
29523 time_t t;
29524 time(&t);
29525 memcpy(zBuf, &t, sizeof(t));
29526 memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid));
29527 assert( sizeof(t)+sizeof(randomnessPid)<=(size_t)nBuf );
29528 nBuf = sizeof(t) + sizeof(randomnessPid);
 
29529 }else{
29530 do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR );
29531 robust_close(0, fd, __LINE__);
29532 }
29533 }
@@ -61200,13 +61220,14 @@
61220 }
61221 p->pNext = db->pVdbe;
61222 p->pPrev = 0;
61223 db->pVdbe = p;
61224 p->magic = VDBE_MAGIC_INIT;
 
61225 p->pParse = pParse;
61226 assert( pParse->aLabel==0 );
61227 assert( pParse->nLabel==0 );
61228 assert( pParse->nOpAlloc==0 );
61229 return p;
61230 }
61231
61232 /*
61233 ** Remember the SQL string for a prepared statement.
@@ -61258,17 +61279,18 @@
61279 ** If an out-of-memory error occurs while resizing the array, return
61280 ** SQLITE_NOMEM. In this case Vdbe.aOp and Vdbe.nOpAlloc remain
61281 ** unchanged (this is so that any opcodes already allocated can be
61282 ** correctly deallocated along with the rest of the Vdbe).
61283 */
61284 static int growOpArray(Vdbe *v){
61285 VdbeOp *pNew;
61286 Parse *p = v->pParse;
61287 int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op)));
61288 pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op));
61289 if( pNew ){
61290 p->nOpAlloc = sqlite3DbMallocSize(p->db, pNew)/sizeof(Op);
61291 v->aOp = pNew;
61292 }
61293 return (pNew ? SQLITE_OK : SQLITE_NOMEM);
61294 }
61295
61296 #ifdef SQLITE_DEBUG
@@ -61303,11 +61325,11 @@
61325 VdbeOp *pOp;
61326
61327 i = p->nOp;
61328 assert( p->magic==VDBE_MAGIC_INIT );
61329 assert( op>0 && op<0xff );
61330 if( p->pParse->nOpAlloc<=i ){
61331 if( growOpArray(p) ){
61332 return 1;
61333 }
61334 }
61335 p->nOp++;
@@ -61414,13 +61436,14 @@
61436 ** always negative and P2 values are suppose to be non-negative.
61437 ** Hence, a negative P2 value is a label that has yet to be resolved.
61438 **
61439 ** Zero is returned if a malloc() fails.
61440 */
61441 SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *v){
61442 Parse *p = v->pParse;
61443 int i = p->nLabel++;
61444 assert( v->magic==VDBE_MAGIC_INIT );
61445 if( (i & (i-1))==0 ){
61446 p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel,
61447 (i*2+1)*sizeof(p->aLabel[0]));
61448 }
61449 if( p->aLabel ){
@@ -61432,16 +61455,17 @@
61455 /*
61456 ** Resolve label "x" to be the address of the next instruction to
61457 ** be inserted. The parameter "x" must have been obtained from
61458 ** a prior call to sqlite3VdbeMakeLabel().
61459 */
61460 SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){
61461 Parse *p = v->pParse;
61462 int j = -1-x;
61463 assert( v->magic==VDBE_MAGIC_INIT );
61464 assert( j<p->nLabel );
61465 if( j>=0 && p->aLabel ){
61466 p->aLabel[j] = v->nOp;
61467 }
61468 }
61469
61470 /*
61471 ** Mark the VDBE as one that can only be run one time.
@@ -61586,11 +61610,12 @@
61610 */
61611 static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
61612 int i;
61613 int nMaxArgs = *pMaxFuncArgs;
61614 Op *pOp;
61615 Parse *pParse = p->pParse;
61616 int *aLabel = pParse->aLabel;
61617 p->readOnly = 1;
61618 p->bIsReader = 0;
61619 for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
61620 u8 opcode = pOp->opcode;
61621
@@ -61649,16 +61674,17 @@
61674 }
61675 }
61676
61677 pOp->opflags = sqlite3OpcodeProperty[opcode];
61678 if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){
61679 assert( -1-pOp->p2<pParse->nLabel );
61680 pOp->p2 = aLabel[-1-pOp->p2];
61681 }
61682 }
61683 sqlite3DbFree(p->db, pParse->aLabel);
61684 pParse->aLabel = 0;
61685 pParse->nLabel = 0;
61686 *pMaxFuncArgs = nMaxArgs;
61687 assert( p->bIsReader!=0 || p->btreeMask==0 );
61688 }
61689
61690 /*
@@ -61698,11 +61724,11 @@
61724 ** address of the first operation added.
61725 */
61726 SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
61727 int addr;
61728 assert( p->magic==VDBE_MAGIC_INIT );
61729 if( p->nOp + nOp > p->pParse->nOpAlloc && growOpArray(p) ){
61730 return 0;
61731 }
61732 addr = p->nOp;
61733 if( ALWAYS(nOp>0) ){
61734 int i;
@@ -61886,10 +61912,17 @@
61912 memset(pOp, 0, sizeof(pOp[0]));
61913 pOp->opcode = OP_Noop;
61914 if( addr==p->nOp-1 ) p->nOp--;
61915 }
61916 }
61917
61918 /*
61919 ** Remove the last opcode inserted
61920 */
61921 SQLITE_PRIVATE void sqlite3VdbeDeleteLastOpcode(Vdbe *p){
61922 p->nOp--;
61923 }
61924
61925 /*
61926 ** Change the value of the P4 operand for a specific instruction.
61927 ** This routine is useful when a large program is loaded from a
61928 ** static array using sqlite3VdbeAddOpList but we want to make a
@@ -62768,10 +62801,11 @@
62801
62802 assert( p!=0 );
62803 assert( p->nOp>0 );
62804 assert( pParse!=0 );
62805 assert( p->magic==VDBE_MAGIC_INIT );
62806 assert( pParse==p->pParse );
62807 db = p->db;
62808 assert( db->mallocFailed==0 );
62809 nVar = pParse->nVar;
62810 nMem = pParse->nMem;
62811 nCursor = pParse->nTab;
@@ -62791,12 +62825,12 @@
62825 nMem += nCursor;
62826
62827 /* Allocate space for memory registers, SQL variables, VDBE cursors and
62828 ** an array to marshal SQL function arguments in.
62829 */
62830 zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */
62831 zEnd = (u8*)&p->aOp[pParse->nOpAlloc]; /* First byte past end of zCsr[] */
62832
62833 resolveP2Values(p, &nArg);
62834 p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort);
62835 if( pParse->explain && nMem<10 ){
62836 nMem = 10;
@@ -63795,11 +63829,10 @@
63829 vdbeFreeOpArray(db, pSub->aOp, pSub->nOp);
63830 sqlite3DbFree(db, pSub);
63831 }
63832 for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
63833 vdbeFreeOpArray(db, p->aOp, p->nOp);
 
63834 sqlite3DbFree(db, p->aColName);
63835 sqlite3DbFree(db, p->zSql);
63836 sqlite3DbFree(db, p->pFree);
63837 #if defined(SQLITE_ENABLE_TREE_EXPLAIN)
63838 sqlite3DbFree(db, p->zExplain);
@@ -76765,20 +76798,29 @@
76798 }
76799 return p;
76800 }
76801
76802 /*
76803 ** If the expression is always either TRUE or FALSE (respectively),
76804 ** then return 1. If one cannot determine the truth value of the
76805 ** expression at compile-time return 0.
76806 **
76807 ** This is an optimization. If is OK to return 0 here even if
76808 ** the expression really is always false or false (a false negative).
76809 ** But it is a bug to return 1 if the expression might have different
76810 ** boolean values in different circumstances (a false positive.)
76811 **
76812 ** Note that if the expression is part of conditional for a
76813 ** LEFT JOIN, then we cannot determine at compile-time whether or not
76814 ** is it true or false, so always return 0.
76815 */
76816 static int exprAlwaysTrue(Expr *p){
76817 int v = 0;
76818 if( ExprHasProperty(p, EP_FromJoin) ) return 0;
76819 if( !sqlite3ExprIsInteger(p, &v) ) return 0;
76820 return v!=0;
76821 }
76822 static int exprAlwaysFalse(Expr *p){
76823 int v = 0;
76824 if( ExprHasProperty(p, EP_FromJoin) ) return 0;
76825 if( !sqlite3ExprIsInteger(p, &v) ) return 0;
76826 return v==0;
@@ -79621,11 +79663,11 @@
79663 sqlite3ExplainPush(pOut);
79664 for(i=0; i<pList->nExpr; i++){
79665 sqlite3ExplainPrintf(pOut, "item[%d] = ", i);
79666 sqlite3ExplainPush(pOut);
79667 sqlite3ExplainExpr(pOut, pList->a[i].pExpr);
79668 sqlite3ExplainPop(pOut, 1);
79669 if( pList->a[i].zName ){
79670 sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName);
79671 }
79672 if( pList->a[i].bSpanIsTab ){
79673 sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan);
@@ -79771,21 +79813,23 @@
79813 op = pExpr->op;
79814 switch( op ){
79815 case TK_AND: {
79816 int d2 = sqlite3VdbeMakeLabel(v);
79817 testcase( jumpIfNull==0 );
 
79818 sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL);
79819 sqlite3ExprCachePush(pParse);
79820 sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
79821 sqlite3VdbeResolveLabel(v, d2);
79822 sqlite3ExprCachePop(pParse, 1);
79823 break;
79824 }
79825 case TK_OR: {
79826 testcase( jumpIfNull==0 );
79827 sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
79828 sqlite3ExprCachePush(pParse);
79829 sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
79830 sqlite3ExprCachePop(pParse, 1);
79831 break;
79832 }
79833 case TK_NOT: {
79834 testcase( jumpIfNull==0 );
79835 sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
@@ -79856,14 +79900,20 @@
79900 sqlite3VdbeResolveLabel(v, destIfFalse);
79901 break;
79902 }
79903 #endif
79904 default: {
79905 if( exprAlwaysTrue(pExpr) ){
79906 sqlite3VdbeAddOp2(v, OP_Goto, 0, dest);
79907 }else if( exprAlwaysFalse(pExpr) ){
79908 /* No-op */
79909 }else{
79910 r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
79911 sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0);
79912 testcase( regFree1==0 );
79913 testcase( jumpIfNull==0 );
79914 }
79915 break;
79916 }
79917 }
79918 sqlite3ReleaseTempReg(pParse, regFree1);
79919 sqlite3ReleaseTempReg(pParse, regFree2);
@@ -79922,18 +79972,20 @@
79972
79973 switch( pExpr->op ){
79974 case TK_AND: {
79975 testcase( jumpIfNull==0 );
79976 sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
79977 sqlite3ExprCachePush(pParse);
79978 sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
79979 sqlite3ExprCachePop(pParse, 1);
79980 break;
79981 }
79982 case TK_OR: {
79983 int d2 = sqlite3VdbeMakeLabel(v);
79984 testcase( jumpIfNull==0 );
 
79985 sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL);
79986 sqlite3ExprCachePush(pParse);
79987 sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
79988 sqlite3VdbeResolveLabel(v, d2);
79989 sqlite3ExprCachePop(pParse, 1);
79990 break;
79991 }
@@ -80001,14 +80053,20 @@
80053 }
80054 break;
80055 }
80056 #endif
80057 default: {
80058 if( exprAlwaysFalse(pExpr) ){
80059 sqlite3VdbeAddOp2(v, OP_Goto, 0, dest);
80060 }else if( exprAlwaysTrue(pExpr) ){
80061 /* no-op */
80062 }else{
80063 r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
80064 sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0);
80065 testcase( regFree1==0 );
80066 testcase( jumpIfNull==0 );
80067 }
80068 break;
80069 }
80070 }
80071 sqlite3ReleaseTempReg(pParse, regFree1);
80072 sqlite3ReleaseTempReg(pParse, regFree2);
@@ -89357,22 +89415,22 @@
89415 nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn;
89416 regBase = sqlite3GetTempRange(pParse, nCol);
89417 for(j=0; j<nCol; j++){
89418 sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j],
89419 regBase+j);
89420 /* If the column affinity is REAL but the number is an integer, then it
89421 ** might be stored in the table as an integer (using a compact
89422 ** representation) then converted to REAL by an OP_RealAffinity opcode.
89423 ** But we are getting ready to store this value back into an index, where
89424 ** it should be converted by to INTEGER again. So omit the OP_RealAffinity
89425 ** opcode if it is present */
89426 if( sqlite3VdbeGetOp(v, -1)->opcode==OP_RealAffinity ){
89427 sqlite3VdbeDeleteLastOpcode(v);
89428 }
89429 }
89430 if( regOut ){
 
 
 
 
 
 
 
 
89431 sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut);
 
89432 }
89433 sqlite3ReleaseTempRange(pParse, regBase, nCol);
89434 return regBase;
89435 }
89436
@@ -93721,10 +93779,11 @@
93779 int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
93780 int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
93781 int ipkTop = 0; /* Top of the rowid change constraint check */
93782 int ipkBottom = 0; /* Bottom of the rowid change constraint check */
93783 u8 isUpdate; /* True if this is an UPDATE operation */
93784 int regRowid = -1; /* Register holding ROWID value */
93785
93786 isUpdate = regOldData!=0;
93787 db = pParse->db;
93788 v = sqlite3GetVdbe(pParse);
93789 assert( v!=0 );
@@ -93951,11 +94010,13 @@
94010 regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn);
94011 for(i=0; i<pIdx->nColumn; i++){
94012 int iField = pIdx->aiColumn[i];
94013 int x;
94014 if( iField<0 || iField==pTab->iPKey ){
94015 if( regRowid==regIdx+i ) continue; /* ROWID already in regIdx+i */
94016 x = regNewData;
94017 regRowid = pIdx->pPartIdxWhere ? -1 : regIdx+i;
94018 }else{
94019 x = iField + regNewData + 1;
94020 }
94021 sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i);
94022 VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName));
@@ -98780,11 +98841,15 @@
98841
98842 /*
98843 ** Free all memory allocations in the pParse object
98844 */
98845 SQLITE_PRIVATE void sqlite3ParserReset(Parse *pParse){
98846 if( pParse ){
98847 sqlite3 *db = pParse->db;
98848 sqlite3DbFree(db, pParse->aLabel);
98849 sqlite3ExprListDelete(db, pParse->pConstExpr);
98850 }
98851 }
98852
98853 /*
98854 ** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
98855 */
@@ -113528,13 +113593,16 @@
113593 sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
113594
113595 /* Special case: a WHERE clause that is constant. Evaluate the
113596 ** expression and either jump over all of the code or fall thru.
113597 */
113598 for(ii=0; ii<sWLB.pWC->nTerm; ii++){
113599 if( nTabList==0 || sqlite3ExprIsConstantNotJoin(sWLB.pWC->a[ii].pExpr) ){
113600 sqlite3ExprIfFalse(pParse, sWLB.pWC->a[ii].pExpr, pWInfo->iBreak,
113601 SQLITE_JUMPIFNULL);
113602 sWLB.pWC->a[ii].wtFlags |= TERM_CODED;
113603 }
113604 }
113605
113606 /* Special case: No FROM clause
113607 */
113608 if( nTabList==0 ){
@@ -121784,11 +121852,11 @@
121852 ** Reset the PRNG back to its uninitialized state. The next call
121853 ** to sqlite3_randomness() will reseed the PRNG using a single call
121854 ** to the xRandomness method of the default VFS.
121855 */
121856 case SQLITE_TESTCTRL_PRNG_RESET: {
121857 sqlite3_randomness(0,0);
121858 break;
121859 }
121860
121861 /*
121862 ** sqlite3_test_control(BITVEC_TEST, size, program)
@@ -124767,10 +124835,23 @@
124835 sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
124836 char **pzErr /* OUT: sqlite3_malloc'd error message */
124837 ){
124838 return fts3InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr);
124839 }
124840
124841 /*
124842 ** Set the pIdxInfo->estimatedRows variable to nRow. Unless this
124843 ** extension is currently being used by a version of SQLite too old to
124844 ** support estimatedRows. In that case this function is a no-op.
124845 */
124846 static void setEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
124847 #if SQLITE_VERSION_NUMBER>=3008002
124848 if( sqlite3_libversion_number()>=3008002 ){
124849 pIdxInfo->estimatedRows = nRow;
124850 }
124851 #endif
124852 }
124853
124854 /*
124855 ** Implementation of the xBestIndex method for FTS3 tables. There
124856 ** are three possible strategies, in order of preference:
124857 **
@@ -124795,11 +124876,24 @@
124876 pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
124877 pInfo->estimatedCost = 5000000;
124878 for(i=0; i<pInfo->nConstraint; i++){
124879 int bDocid; /* True if this constraint is on docid */
124880 struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i];
124881 if( pCons->usable==0 ){
124882 if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
124883 /* There exists an unusable MATCH constraint. This means that if
124884 ** the planner does elect to use the results of this call as part
124885 ** of the overall query plan the user will see an "unable to use
124886 ** function MATCH in the requested context" error. To discourage
124887 ** this, return a very high cost here. */
124888 pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
124889 pInfo->estimatedCost = 1e50;
124890 setEstimatedRows(pInfo, ((sqlite3_int64)1) << 50);
124891 return SQLITE_OK;
124892 }
124893 continue;
124894 }
124895
124896 bDocid = (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1);
124897
124898 /* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */
124899 if( iCons<0 && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && bDocid ){
124900
+7 -5
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -107,11 +107,11 @@
107107
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108108
** [sqlite_version()] and [sqlite_source_id()].
109109
*/
110110
#define SQLITE_VERSION "3.8.3"
111111
#define SQLITE_VERSION_NUMBER 3008003
112
-#define SQLITE_SOURCE_ID "2013-12-23 11:33:32 25b8a1c9ba77df3b7c78cbce922cb593d661696d"
112
+#define SQLITE_SOURCE_ID "2014-01-04 15:17:04 4e725f53131d3584319c710c8710a068989543c6"
113113
114114
/*
115115
** CAPI3REF: Run-Time Library Version Numbers
116116
** KEYWORDS: sqlite3_version, sqlite3_sourceid
117117
**
@@ -2398,15 +2398,17 @@
23982398
** already uses the largest possible [ROWID]. The PRNG is also used for
23992399
** the build-in random() and randomblob() SQL functions. This interface allows
24002400
** applications to access the same PRNG for other purposes.
24012401
**
24022402
** ^A call to this routine stores N bytes of randomness into buffer P.
2403
+** ^If N is less than one, then P can be a NULL pointer.
24032404
**
2404
-** ^The first time this routine is invoked (either internally or by
2405
-** the application) the PRNG is seeded using randomness obtained
2406
-** from the xRandomness method of the default [sqlite3_vfs] object.
2407
-** ^On all subsequent invocations, the pseudo-randomness is generated
2405
+** ^If this routine has not been previously called or if the previous
2406
+** call had N less than one, then the PRNG is seeded using randomness
2407
+** obtained from the xRandomness method of the default [sqlite3_vfs] object.
2408
+** ^If the previous call to this routine had an N of 1 or more then
2409
+** the pseudo-randomness is generated
24082410
** internally and without recourse to the [sqlite3_vfs] xRandomness
24092411
** method.
24102412
*/
24112413
SQLITE_API void sqlite3_randomness(int N, void *P);
24122414
24132415
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -107,11 +107,11 @@
107 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108 ** [sqlite_version()] and [sqlite_source_id()].
109 */
110 #define SQLITE_VERSION "3.8.3"
111 #define SQLITE_VERSION_NUMBER 3008003
112 #define SQLITE_SOURCE_ID "2013-12-23 11:33:32 25b8a1c9ba77df3b7c78cbce922cb593d661696d"
113
114 /*
115 ** CAPI3REF: Run-Time Library Version Numbers
116 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
117 **
@@ -2398,15 +2398,17 @@
2398 ** already uses the largest possible [ROWID]. The PRNG is also used for
2399 ** the build-in random() and randomblob() SQL functions. This interface allows
2400 ** applications to access the same PRNG for other purposes.
2401 **
2402 ** ^A call to this routine stores N bytes of randomness into buffer P.
 
2403 **
2404 ** ^The first time this routine is invoked (either internally or by
2405 ** the application) the PRNG is seeded using randomness obtained
2406 ** from the xRandomness method of the default [sqlite3_vfs] object.
2407 ** ^On all subsequent invocations, the pseudo-randomness is generated
 
2408 ** internally and without recourse to the [sqlite3_vfs] xRandomness
2409 ** method.
2410 */
2411 SQLITE_API void sqlite3_randomness(int N, void *P);
2412
2413
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -107,11 +107,11 @@
107 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108 ** [sqlite_version()] and [sqlite_source_id()].
109 */
110 #define SQLITE_VERSION "3.8.3"
111 #define SQLITE_VERSION_NUMBER 3008003
112 #define SQLITE_SOURCE_ID "2014-01-04 15:17:04 4e725f53131d3584319c710c8710a068989543c6"
113
114 /*
115 ** CAPI3REF: Run-Time Library Version Numbers
116 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
117 **
@@ -2398,15 +2398,17 @@
2398 ** already uses the largest possible [ROWID]. The PRNG is also used for
2399 ** the build-in random() and randomblob() SQL functions. This interface allows
2400 ** applications to access the same PRNG for other purposes.
2401 **
2402 ** ^A call to this routine stores N bytes of randomness into buffer P.
2403 ** ^If N is less than one, then P can be a NULL pointer.
2404 **
2405 ** ^If this routine has not been previously called or if the previous
2406 ** call had N less than one, then the PRNG is seeded using randomness
2407 ** obtained from the xRandomness method of the default [sqlite3_vfs] object.
2408 ** ^If the previous call to this routine had an N of 1 or more then
2409 ** the pseudo-randomness is generated
2410 ** internally and without recourse to the [sqlite3_vfs] xRandomness
2411 ** method.
2412 */
2413 SQLITE_API void sqlite3_randomness(int N, void *P);
2414
2415
+98 -3
--- src/style.c
+++ src/style.c
@@ -243,10 +243,41 @@
243243
va_start(ap, zFormat);
244244
local_zCurrentPage = vmprintf(zFormat, ap);
245245
va_end(ap);
246246
}
247247
}
248
+
249
+/*
250
+** Create a TH1 variable containing the URL for the specified config resource.
251
+** The resulting variable name will be of the form $[zVarPrefix]_url.
252
+*/
253
+static void url_var(
254
+ const char *zVarPrefix,
255
+ const char *zConfigName,
256
+ const char *zPageName
257
+){
258
+ char *zMtime = db_get_mtime(zConfigName, 0, 0);
259
+ char *zUrl = mprintf("%s/%s/%s%.5s", g.zTop, zPageName, zMtime,
260
+ MANIFEST_UUID);
261
+ char *zVarName = mprintf("%s_url", zVarPrefix);
262
+ Th_Store(zVarName, zUrl);
263
+ free(zMtime);
264
+ free(zUrl);
265
+ free(zVarName);
266
+}
267
+
268
+/*
269
+** Create a TH1 variable containing the URL for the specified config image.
270
+** The resulting variable name will be of the form $[zImageName]_image_url.
271
+*/
272
+static void image_url_var(const char *zImageName){
273
+ char *zVarPrefix = mprintf("%s_image", zImageName);
274
+ char *zConfigName = mprintf("%s-image", zImageName);
275
+ url_var(zVarPrefix, zConfigName, zImageName);
276
+ free(zVarPrefix);
277
+ free(zConfigName);
278
+}
248279
249280
/*
250281
** Draw the header.
251282
*/
252283
void style_header(const char *zTitleFormat, ...){
@@ -275,10 +306,13 @@
275306
Th_Store("csrf_token", g.zCsrfToken);
276307
Th_Store("release_version", RELEASE_VERSION);
277308
Th_Store("manifest_version", MANIFEST_VERSION);
278309
Th_Store("manifest_date", MANIFEST_DATE);
279310
Th_Store("compiler_name", COMPILER_NAME);
311
+ url_var("stylesheet", "css", "style.css");
312
+ image_url_var("logo");
313
+ image_url_var("background");
280314
if( g.zLogin ){
281315
Th_Store("login", g.zLogin);
282316
}
283317
if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
284318
Th_Render(zHeader);
@@ -406,17 +440,17 @@
406440
@ <head>
407441
@ <base href="$baseurl/$current_page" />
408442
@ <title>$<project_name>: $<title></title>
409443
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
410444
@ href="$home/timeline.rss" />
411
-@ <link rel="stylesheet" href="$home/style.css?default" type="text/css"
445
+@ <link rel="stylesheet" href="$stylesheet_url" type="text/css"
412446
@ media="screen" />
413447
@ </head>
414448
@ <body>
415449
@ <div class="header">
416450
@ <div class="logo">
417
-@ <img src="$home/logo" alt="logo" />
451
+@ <img src="$logo_image_url" alt="logo" />
418452
@ </div>
419453
@ <div class="title"><small>$<project_name></small><br />$<title></div>
420454
@ <div class="status"><th1>
421455
@ if {[info exists login]} {
422456
@ puts "Logged in as $login"
@@ -430,11 +464,11 @@
430464
@ html "<a href='$home$index_page'>Home</a>\n"
431465
@ if {[anycap jor]} {
432466
@ html "<a href='$home/timeline'>Timeline</a>\n"
433467
@ }
434468
@ if {[hascap oh]} {
435
-@ html "<a href='$home/dir?ci=tip'>Files</a>\n"
469
+@ html "<a href='$home/tree?ci=tip'>Files</a>\n"
436470
@ }
437471
@ if {[hascap o]} {
438472
@ html "<a href='$home/brlist'>Branches</a>\n"
439473
@ html "<a href='$home/taglist'>Tags</a>\n"
440474
@ }
@@ -737,10 +771,69 @@
737771
"format for the list in the file browser",
738772
@ margin-left: 0.5em;
739773
@ padding-left: 0.5em;
740774
@ white-space: nowrap;
741775
},
776
+ { ".filetree",
777
+ "tree-view file browser",
778
+ @ margin: 1em 0;
779
+ @ line-height: 1.5;
780
+ },
781
+ { ".filetree ul",
782
+ "tree-view lists",
783
+ @ margin: 0;
784
+ @ padding: 0;
785
+ @ list-style: none;
786
+ },
787
+ { ".filetree ul ul",
788
+ "tree-view lists below the root",
789
+ @ position: relative;
790
+ @ margin: 0 0 0 21px;
791
+ },
792
+ { ".filetree li",
793
+ "tree-view lists items",
794
+ @ position: relative;
795
+ },
796
+ { ".filetree li li:before",
797
+ "tree-view node lines",
798
+ @ content: '';
799
+ @ position: absolute;
800
+ @ top: -.8em;
801
+ @ left: -14px;
802
+ @ width: 14px;
803
+ @ height: 1.5em;
804
+ @ border-left: 2px solid #aaa;
805
+ @ border-bottom: 2px solid #aaa;
806
+ },
807
+ { ".filetree ul ul:before",
808
+ "tree-view directory lines",
809
+ @ content: '';
810
+ @ position: absolute;
811
+ @ top: -1.5em;
812
+ @ bottom: 0;
813
+ @ left: -35px;
814
+ @ border-left: 2px solid #aaa;
815
+ },
816
+ { ".filetree li:last-child > ul:before",
817
+ "hide lines for last-child directories",
818
+ @ display: none;
819
+ },
820
+ { ".filetree a",
821
+ "tree-view links",
822
+ @ position: relative;
823
+ @ z-index: 1;
824
+ @ display: inline-block;
825
+ @ min-height: 16px;
826
+ @ padding-left: 21px;
827
+ @ background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP\/\/\/yEhIf\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAIvlIKpxqcfmgOUvoaqDSCxrEEfF14GqFXImJZsu73wepJzVMNxrtNTj3NATMKhpwAAOw==);
828
+ @ background-position: center left;
829
+ @ background-repeat: no-repeat;
830
+ },
831
+ { ".filetree .dir > a",
832
+ "tree-view directory links",
833
+ @ background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP/WVCIiIv\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaXpo+jUs6b5Z/K4siDu5RPUFADs=);
834
+ },
742835
{ "table.login_out",
743836
"table format for login/out label/input table",
744837
@ text-align: left;
745838
@ margin-right: 10px;
746839
@ margin-left: 10px;
@@ -1151,10 +1244,12 @@
11511244
/* Process through TH1 in order to give an opportunity to substitute
11521245
** variables such as $baseurl.
11531246
*/
11541247
Th_Store("baseurl", g.zBaseURL);
11551248
Th_Store("home", g.zTop);
1249
+ image_url_var("logo");
1250
+ image_url_var("background");
11561251
Th_Render(blob_str(&css));
11571252
11581253
/* Tell CGI that the content returned by this page is considered cacheable */
11591254
g.isConst = 1;
11601255
}
11611256
--- src/style.c
+++ src/style.c
@@ -243,10 +243,41 @@
243 va_start(ap, zFormat);
244 local_zCurrentPage = vmprintf(zFormat, ap);
245 va_end(ap);
246 }
247 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
249 /*
250 ** Draw the header.
251 */
252 void style_header(const char *zTitleFormat, ...){
@@ -275,10 +306,13 @@
275 Th_Store("csrf_token", g.zCsrfToken);
276 Th_Store("release_version", RELEASE_VERSION);
277 Th_Store("manifest_version", MANIFEST_VERSION);
278 Th_Store("manifest_date", MANIFEST_DATE);
279 Th_Store("compiler_name", COMPILER_NAME);
 
 
 
280 if( g.zLogin ){
281 Th_Store("login", g.zLogin);
282 }
283 if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
284 Th_Render(zHeader);
@@ -406,17 +440,17 @@
406 @ <head>
407 @ <base href="$baseurl/$current_page" />
408 @ <title>$<project_name>: $<title></title>
409 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
410 @ href="$home/timeline.rss" />
411 @ <link rel="stylesheet" href="$home/style.css?default" type="text/css"
412 @ media="screen" />
413 @ </head>
414 @ <body>
415 @ <div class="header">
416 @ <div class="logo">
417 @ <img src="$home/logo" alt="logo" />
418 @ </div>
419 @ <div class="title"><small>$<project_name></small><br />$<title></div>
420 @ <div class="status"><th1>
421 @ if {[info exists login]} {
422 @ puts "Logged in as $login"
@@ -430,11 +464,11 @@
430 @ html "<a href='$home$index_page'>Home</a>\n"
431 @ if {[anycap jor]} {
432 @ html "<a href='$home/timeline'>Timeline</a>\n"
433 @ }
434 @ if {[hascap oh]} {
435 @ html "<a href='$home/dir?ci=tip'>Files</a>\n"
436 @ }
437 @ if {[hascap o]} {
438 @ html "<a href='$home/brlist'>Branches</a>\n"
439 @ html "<a href='$home/taglist'>Tags</a>\n"
440 @ }
@@ -737,10 +771,69 @@
737 "format for the list in the file browser",
738 @ margin-left: 0.5em;
739 @ padding-left: 0.5em;
740 @ white-space: nowrap;
741 },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
742 { "table.login_out",
743 "table format for login/out label/input table",
744 @ text-align: left;
745 @ margin-right: 10px;
746 @ margin-left: 10px;
@@ -1151,10 +1244,12 @@
1151 /* Process through TH1 in order to give an opportunity to substitute
1152 ** variables such as $baseurl.
1153 */
1154 Th_Store("baseurl", g.zBaseURL);
1155 Th_Store("home", g.zTop);
 
 
1156 Th_Render(blob_str(&css));
1157
1158 /* Tell CGI that the content returned by this page is considered cacheable */
1159 g.isConst = 1;
1160 }
1161
--- src/style.c
+++ src/style.c
@@ -243,10 +243,41 @@
243 va_start(ap, zFormat);
244 local_zCurrentPage = vmprintf(zFormat, ap);
245 va_end(ap);
246 }
247 }
248
249 /*
250 ** Create a TH1 variable containing the URL for the specified config resource.
251 ** The resulting variable name will be of the form $[zVarPrefix]_url.
252 */
253 static void url_var(
254 const char *zVarPrefix,
255 const char *zConfigName,
256 const char *zPageName
257 ){
258 char *zMtime = db_get_mtime(zConfigName, 0, 0);
259 char *zUrl = mprintf("%s/%s/%s%.5s", g.zTop, zPageName, zMtime,
260 MANIFEST_UUID);
261 char *zVarName = mprintf("%s_url", zVarPrefix);
262 Th_Store(zVarName, zUrl);
263 free(zMtime);
264 free(zUrl);
265 free(zVarName);
266 }
267
268 /*
269 ** Create a TH1 variable containing the URL for the specified config image.
270 ** The resulting variable name will be of the form $[zImageName]_image_url.
271 */
272 static void image_url_var(const char *zImageName){
273 char *zVarPrefix = mprintf("%s_image", zImageName);
274 char *zConfigName = mprintf("%s-image", zImageName);
275 url_var(zVarPrefix, zConfigName, zImageName);
276 free(zVarPrefix);
277 free(zConfigName);
278 }
279
280 /*
281 ** Draw the header.
282 */
283 void style_header(const char *zTitleFormat, ...){
@@ -275,10 +306,13 @@
306 Th_Store("csrf_token", g.zCsrfToken);
307 Th_Store("release_version", RELEASE_VERSION);
308 Th_Store("manifest_version", MANIFEST_VERSION);
309 Th_Store("manifest_date", MANIFEST_DATE);
310 Th_Store("compiler_name", COMPILER_NAME);
311 url_var("stylesheet", "css", "style.css");
312 image_url_var("logo");
313 image_url_var("background");
314 if( g.zLogin ){
315 Th_Store("login", g.zLogin);
316 }
317 if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
318 Th_Render(zHeader);
@@ -406,17 +440,17 @@
440 @ <head>
441 @ <base href="$baseurl/$current_page" />
442 @ <title>$<project_name>: $<title></title>
443 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
444 @ href="$home/timeline.rss" />
445 @ <link rel="stylesheet" href="$stylesheet_url" type="text/css"
446 @ media="screen" />
447 @ </head>
448 @ <body>
449 @ <div class="header">
450 @ <div class="logo">
451 @ <img src="$logo_image_url" alt="logo" />
452 @ </div>
453 @ <div class="title"><small>$<project_name></small><br />$<title></div>
454 @ <div class="status"><th1>
455 @ if {[info exists login]} {
456 @ puts "Logged in as $login"
@@ -430,11 +464,11 @@
464 @ html "<a href='$home$index_page'>Home</a>\n"
465 @ if {[anycap jor]} {
466 @ html "<a href='$home/timeline'>Timeline</a>\n"
467 @ }
468 @ if {[hascap oh]} {
469 @ html "<a href='$home/tree?ci=tip'>Files</a>\n"
470 @ }
471 @ if {[hascap o]} {
472 @ html "<a href='$home/brlist'>Branches</a>\n"
473 @ html "<a href='$home/taglist'>Tags</a>\n"
474 @ }
@@ -737,10 +771,69 @@
771 "format for the list in the file browser",
772 @ margin-left: 0.5em;
773 @ padding-left: 0.5em;
774 @ white-space: nowrap;
775 },
776 { ".filetree",
777 "tree-view file browser",
778 @ margin: 1em 0;
779 @ line-height: 1.5;
780 },
781 { ".filetree ul",
782 "tree-view lists",
783 @ margin: 0;
784 @ padding: 0;
785 @ list-style: none;
786 },
787 { ".filetree ul ul",
788 "tree-view lists below the root",
789 @ position: relative;
790 @ margin: 0 0 0 21px;
791 },
792 { ".filetree li",
793 "tree-view lists items",
794 @ position: relative;
795 },
796 { ".filetree li li:before",
797 "tree-view node lines",
798 @ content: '';
799 @ position: absolute;
800 @ top: -.8em;
801 @ left: -14px;
802 @ width: 14px;
803 @ height: 1.5em;
804 @ border-left: 2px solid #aaa;
805 @ border-bottom: 2px solid #aaa;
806 },
807 { ".filetree ul ul:before",
808 "tree-view directory lines",
809 @ content: '';
810 @ position: absolute;
811 @ top: -1.5em;
812 @ bottom: 0;
813 @ left: -35px;
814 @ border-left: 2px solid #aaa;
815 },
816 { ".filetree li:last-child > ul:before",
817 "hide lines for last-child directories",
818 @ display: none;
819 },
820 { ".filetree a",
821 "tree-view links",
822 @ position: relative;
823 @ z-index: 1;
824 @ display: inline-block;
825 @ min-height: 16px;
826 @ padding-left: 21px;
827 @ background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP\/\/\/yEhIf\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAIvlIKpxqcfmgOUvoaqDSCxrEEfF14GqFXImJZsu73wepJzVMNxrtNTj3NATMKhpwAAOw==);
828 @ background-position: center left;
829 @ background-repeat: no-repeat;
830 },
831 { ".filetree .dir > a",
832 "tree-view directory links",
833 @ background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP/WVCIiIv\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaXpo+jUs6b5Z/K4siDu5RPUFADs=);
834 },
835 { "table.login_out",
836 "table format for login/out label/input table",
837 @ text-align: left;
838 @ margin-right: 10px;
839 @ margin-left: 10px;
@@ -1151,10 +1244,12 @@
1244 /* Process through TH1 in order to give an opportunity to substitute
1245 ** variables such as $baseurl.
1246 */
1247 Th_Store("baseurl", g.zBaseURL);
1248 Th_Store("home", g.zTop);
1249 image_url_var("logo");
1250 image_url_var("background");
1251 Th_Render(blob_str(&css));
1252
1253 /* Tell CGI that the content returned by this page is considered cacheable */
1254 g.isConst = 1;
1255 }
1256
+3 -2
--- src/tar.c
+++ src/tar.c
@@ -336,12 +336,13 @@
336336
unsigned int mTime /* Modification time */
337337
){
338338
int i;
339339
for(i=nName-1; i>0 && zName[i]!='/'; i--){}
340340
if( i<=0 ) return;
341
- if( i < tball.nPrevDirAlloc && tball.zPrevDir[i]==0 &&
342
- memcmp(tball.zPrevDir, zName, i)==0 ) return;
341
+ if( i<tball.nPrevDirAlloc
342
+ && strncmp(tball.zPrevDir, zName, i)==0
343
+ && tball.zPrevDir[i]==0 ) return;
343344
db_multi_exec("INSERT OR IGNORE INTO dir VALUES('%#q')", i, zName);
344345
if( sqlite3_changes(g.db)==0 ) return;
345346
tar_add_directory_of(zName, i-1, mTime);
346347
tar_add_header(zName, i, 0755, mTime, 0, '5');
347348
if( i >= tball.nPrevDirAlloc ){
348349
--- src/tar.c
+++ src/tar.c
@@ -336,12 +336,13 @@
336 unsigned int mTime /* Modification time */
337 ){
338 int i;
339 for(i=nName-1; i>0 && zName[i]!='/'; i--){}
340 if( i<=0 ) return;
341 if( i < tball.nPrevDirAlloc && tball.zPrevDir[i]==0 &&
342 memcmp(tball.zPrevDir, zName, i)==0 ) return;
 
343 db_multi_exec("INSERT OR IGNORE INTO dir VALUES('%#q')", i, zName);
344 if( sqlite3_changes(g.db)==0 ) return;
345 tar_add_directory_of(zName, i-1, mTime);
346 tar_add_header(zName, i, 0755, mTime, 0, '5');
347 if( i >= tball.nPrevDirAlloc ){
348
--- src/tar.c
+++ src/tar.c
@@ -336,12 +336,13 @@
336 unsigned int mTime /* Modification time */
337 ){
338 int i;
339 for(i=nName-1; i>0 && zName[i]!='/'; i--){}
340 if( i<=0 ) return;
341 if( i<tball.nPrevDirAlloc
342 && strncmp(tball.zPrevDir, zName, i)==0
343 && tball.zPrevDir[i]==0 ) return;
344 db_multi_exec("INSERT OR IGNORE INTO dir VALUES('%#q')", i, zName);
345 if( sqlite3_changes(g.db)==0 ) return;
346 tar_add_directory_of(zName, i-1, mTime);
347 tar_add_header(zName, i, 0755, mTime, 0, '5');
348 if( i >= tball.nPrevDirAlloc ){
349
+263 -162
--- src/th.c
+++ src/th.c
@@ -1,8 +1,8 @@
11
22
/*
3
-** The implementation of the TH core. This file contains the parser, and
3
+** The implementation of the TH core. This file contains the parser, and
44
** the implementation of the interface in th.h.
55
*/
66
77
#include "config.h"
88
#include "th.h"
@@ -16,11 +16,11 @@
1616
/*
1717
** Interpreter structure.
1818
*/
1919
struct Th_Interp {
2020
Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */
21
- char *zResult; /* Current interpreter result (Th_Malloc()ed) */
21
+ char *zResult; /* Current interpreter result (Th_Malloc()ed) */
2222
int nResult; /* number of bytes in zResult */
2323
Th_Hash *paCmd; /* Table of registered commands */
2424
Th_Frame *pFrame; /* Current execution frame */
2525
int isListMode; /* True if thSplitList() should operate in "list" mode */
2626
};
@@ -42,25 +42,25 @@
4242
** are stored in the Th_Frame.paVar hash table member of the associated
4343
** stack frame object.
4444
**
4545
** When an interpreter is created, a single Th_Frame structure is also
4646
** allocated - the global variable scope. Th_Interp.pFrame (the current
47
-** interpreter frame) is initialised to point to this Th_Frame. It is
48
-** not deleted for the lifetime of the interpreter (because the global
47
+** interpreter frame) is initialised to point to this Th_Frame. It is
48
+** not deleted for the lifetime of the interpreter (because the global
4949
** frame never goes out of scope).
5050
**
5151
** New stack frames are created by the Th_InFrame() function. Before
5252
** invoking its callback function, Th_InFrame() allocates a new Th_Frame
5353
** structure with pCaller set to the current frame (Th_Interp.pFrame),
5454
** and sets the current frame to the new frame object. After the callback
5555
** has been invoked, the allocated Th_Frame is deleted and the value
5656
** of the current frame pointer restored.
57
-**
58
-** By default, the Th_SetVar(), Th_UnsetVar() and Th_GetVar() functions
59
-** access variable values in the current frame. If they need to access
57
+**
58
+** By default, the Th_SetVar(), Th_UnsetVar() and Th_GetVar() functions
59
+** access variable values in the current frame. If they need to access
6060
** the global frame, they do so by traversing the pCaller pointer list.
61
-** Likewise, the Th_LinkVar() function uses the pCaller pointers to
61
+** Likewise, the Th_LinkVar() function uses the pCaller pointers to
6262
** link to variables located in the global or other stack frames.
6363
*/
6464
struct Th_Frame {
6565
Th_Hash *paVar; /* Variables defined in this scope */
6666
Th_Frame *pCaller; /* Calling frame */
@@ -84,11 +84,11 @@
8484
** value.
8585
*/
8686
struct Th_Variable {
8787
int nRef; /* Number of references to this structure */
8888
int nData; /* Number of bytes at Th_Variable.zData */
89
- char *zData; /* Data for scalar variables */
89
+ char *zData; /* Data for scalar variables */
9090
Th_Hash *pHash; /* Data for array variables */
9191
};
9292
9393
/*
9494
** Hash table API:
@@ -105,24 +105,24 @@
105105
static int thEndOfLine(const char *, int);
106106
107107
static int thPushFrame(Th_Interp*, Th_Frame*);
108108
static void thPopFrame(Th_Interp*);
109109
110
-static void thFreeVariable(Th_HashEntry*, void*);
111
-static void thFreeCommand(Th_HashEntry*, void*);
110
+static int thFreeVariable(Th_HashEntry*, void*);
111
+static int thFreeCommand(Th_HashEntry*, void*);
112112
113113
/*
114114
** The following are used by both the expression and language parsers.
115
-** Given that the start of the input string (z, n) is a language
115
+** Given that the start of the input string (z, n) is a language
116116
** construct of the relevant type (a command enclosed in [], an escape
117117
** sequence etc.), these functions determine the number of bytes
118118
** of the input consumed by the construct. For example:
119119
**
120120
** int nByte;
121121
** thNextCommand(interp, "[expr $a+1] $nIter", 18, &nByte);
122122
**
123
-** results in variable nByte being set to 11. Or,
123
+** results in variable nByte being set to 11. Or,
124124
**
125125
** thNextVarname(interp, "$a+1", 4, &nByte);
126126
**
127127
** results in nByte being set to 2.
128128
*/
@@ -132,24 +132,24 @@
132132
static int thNextNumber (Th_Interp*, const char *z, int n, int *pN);
133133
static int thNextSpace (Th_Interp*, const char *z, int n, int *pN);
134134
135135
/*
136136
** Given that the input string (z, n) contains a language construct of
137
-** the relevant type (a command enclosed in [], an escape sequence
137
+** the relevant type (a command enclosed in [], an escape sequence
138138
** like "\xFF" or a variable reference like "${varname}", perform
139139
** substitution on the string and store the resulting string in
140140
** the interpreter result.
141141
*/
142142
static int thSubstCommand(Th_Interp*, const char *z, int n);
143143
static int thSubstEscape (Th_Interp*, const char *z, int n);
144144
static int thSubstVarname(Th_Interp*, const char *z, int n);
145145
146146
/*
147
-** Given that there is a th1 word located at the start of the input
147
+** Given that there is a th1 word located at the start of the input
148148
** string (z, n), determine the length in bytes of that word. If the
149
-** isCmd argument is non-zero, then an unescaped ";" byte not
150
-** located inside of a block or quoted string is considered to mark
149
+** isCmd argument is non-zero, then an unescaped ";" byte not
150
+** located inside of a block or quoted string is considered to mark
151151
** the end of the word.
152152
*/
153153
static int thNextWord(Th_Interp*, const char *z, int n, int *pN, int isCmd);
154154
155155
/*
@@ -176,13 +176,13 @@
176176
** Append nAdd bytes of content copied from zAdd to the end of buffer
177177
** pBuffer. If there is not enough space currently allocated, resize
178178
** the allocation to make space.
179179
*/
180180
static int thBufferWrite(
181
- Th_Interp *interp,
182
- Buffer *pBuffer,
183
- const char *zAdd,
181
+ Th_Interp *interp,
182
+ Buffer *pBuffer,
183
+ const char *zAdd,
184184
int nAdd
185185
){
186186
int nReq;
187187
188188
if( nAdd<0 ){
@@ -258,12 +258,14 @@
258258
** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259259
** structure that the entry points to. Free the Th_Variable if its
260260
** reference count reaches 0.
261261
**
262262
** Argument pContext is a pointer to the interpreter structure.
263
+**
264
+** Returns non-zero if the Th_Variable was actually freed.
263265
*/
264
-static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
266
+static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265267
Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266268
pValue->nRef--;
267269
assert( pValue->nRef>=0 );
268270
if( pValue->nRef==0 ){
269271
Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271273
if( pValue->pHash ){
272274
Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273275
Th_HashDelete(interp, pValue->pHash);
274276
}
275277
Th_Free(interp, pValue);
278
+ pEntry->pData = 0;
279
+ return 1;
276280
}
281
+ return 0;
277282
}
278283
279284
/*
280285
** Argument pEntry points to an entry in the command hash table
281286
** (Th_Interp.paCmd). Delete the Th_Command structure that the
282287
** entry points to.
283288
**
284289
** Argument pContext is a pointer to the interpreter structure.
290
+**
291
+** Always returns non-zero.
285292
*/
286
-static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
293
+static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287294
Th_Command *pCommand = (Th_Command *)pEntry->pData;
288295
if( pCommand->xDel ){
289296
pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290297
}
291298
Th_Free((Th_Interp *)pContext, pEntry->pData);
292299
pEntry->pData = 0;
300
+ return 1;
293301
}
294302
295303
/*
296304
** Push a new frame onto the stack.
297305
*/
@@ -311,19 +319,19 @@
311319
Th_HashDelete(interp, pFrame->paVar);
312320
interp->pFrame = pFrame->pCaller;
313321
}
314322
315323
/*
316
-** The first part of the string (zInput,nInput) contains an escape
324
+** The first part of the string (zInput,nInput) contains an escape
317325
** sequence. Set *pnEscape to the number of bytes in the escape sequence.
318326
** If there is a parse error, return TH_ERROR and set the interpreter
319327
** result to an error message. Otherwise return TH_OK.
320328
*/
321329
static int thNextEscape(
322330
Th_Interp *interp,
323
- const char *zInput,
324
- int nInput,
331
+ const char *zInput,
332
+ int nInput,
325333
int *pnEscape
326334
){
327335
int i = 2;
328336
329337
assert(nInput>0);
@@ -344,18 +352,18 @@
344352
return TH_OK;
345353
}
346354
347355
/*
348356
** The first part of the string (zInput,nInput) contains a variable
349
-** reference. Set *pnVarname to the number of bytes in the variable
350
-** reference. If there is a parse error, return TH_ERROR and set the
357
+** reference. Set *pnVarname to the number of bytes in the variable
358
+** reference. If there is a parse error, return TH_ERROR and set the
351359
** interpreter result to an error message. Otherwise return TH_OK.
352360
*/
353361
int thNextVarname(
354362
Th_Interp *interp,
355
- const char *zInput,
356
- int nInput,
363
+ const char *zInput,
364
+ int nInput,
357365
int *pnVarname
358366
){
359367
int i;
360368
361369
assert(nInput>0);
@@ -401,19 +409,19 @@
401409
return TH_OK;
402410
}
403411
404412
/*
405413
** The first part of the string (zInput,nInput) contains a command
406
-** enclosed in a "[]" block. Set *pnCommand to the number of bytes in
407
-** the variable reference. If there is a parse error, return TH_ERROR
408
-** and set the interpreter result to an error message. Otherwise return
414
+** enclosed in a "[]" block. Set *pnCommand to the number of bytes in
415
+** the variable reference. If there is a parse error, return TH_ERROR
416
+** and set the interpreter result to an error message. Otherwise return
409417
** TH_OK.
410418
*/
411419
int thNextCommand(
412420
Th_Interp *interp,
413
- const char *zInput,
414
- int nInput,
421
+ const char *zInput,
422
+ int nInput,
415423
int *pnCommand
416424
){
417425
int nBrace = 0;
418426
int nSquare = 0;
419427
int i;
@@ -438,17 +446,17 @@
438446
439447
return TH_OK;
440448
}
441449
442450
/*
443
-** Set *pnSpace to the number of whitespace bytes at the start of
451
+** Set *pnSpace to the number of whitespace bytes at the start of
444452
** input string (zInput, nInput). Always return TH_OK.
445453
*/
446454
int thNextSpace(
447455
Th_Interp *interp,
448
- const char *zInput,
449
- int nInput,
456
+ const char *zInput,
457
+ int nInput,
450458
int *pnSpace
451459
){
452460
int i;
453461
for(i=0; i<nInput && th_isspace(zInput[i]); i++);
454462
*pnSpace = i;
@@ -457,21 +465,21 @@
457465
458466
/*
459467
** The first byte of the string (zInput,nInput) is not white-space.
460468
** Set *pnWord to the number of bytes in the th1 word that starts
461469
** with this byte. If a complete word cannot be parsed or some other
462
-** error occurs, return TH_ERROR and set the interpreter result to
470
+** error occurs, return TH_ERROR and set the interpreter result to
463471
** an error message. Otherwise return TH_OK.
464472
**
465
-** If the isCmd argument is non-zero, then an unescaped ";" byte not
466
-** located inside of a block or quoted string is considered to mark
473
+** If the isCmd argument is non-zero, then an unescaped ";" byte not
474
+** located inside of a block or quoted string is considered to mark
467475
** the end of the word.
468476
*/
469477
static int thNextWord(
470478
Th_Interp *interp,
471
- const char *zInput,
472
- int nInput,
479
+ const char *zInput,
480
+ int nInput,
473481
int *pnWord,
474482
int isCmd
475483
){
476484
int iEnd = 0;
477485
@@ -531,12 +539,12 @@
531539
return thEvalLocal(interp, &zWord[1], nWord-2);
532540
}
533541
534542
/*
535543
** The input string (zWord, nWord) contains a th1 variable reference
536
-** (a '$' byte followed by a variable name). Perform substitution on
537
-** the input string and store the resulting string in the interpreter
544
+** (a '$' byte followed by a variable name). Perform substitution on
545
+** the input string and store the resulting string in the interpreter
538546
** result.
539547
*/
540548
static int thSubstVarname(
541549
Th_Interp *interp,
542550
const char *zWord,
@@ -572,11 +580,11 @@
572580
return Th_GetVar(interp, &zWord[1], nWord-1);
573581
}
574582
575583
/*
576584
** The input string (zWord, nWord) contains a th1 escape sequence.
577
-** Perform substitution on the input string and store the resulting
585
+** Perform substitution on the input string and store the resulting
578586
** string in the interpreter result.
579587
*/
580588
static int thSubstEscape(
581589
Th_Interp *interp,
582590
const char *zWord,
@@ -608,11 +616,11 @@
608616
return TH_OK;
609617
}
610618
611619
/*
612620
** The input string (zWord, nWord) contains a th1 word. Perform
613
-** substitution on the input string and store the resulting
621
+** substitution on the input string and store the resulting
614622
** string in the interpreter result.
615623
*/
616624
static int thSubstWord(
617625
Th_Interp *interp,
618626
const char *zWord,
@@ -640,20 +648,20 @@
640648
int (*xGet)(Th_Interp *, const char*, int, int *) = 0;
641649
int (*xSubst)(Th_Interp *, const char*, int) = 0;
642650
643651
switch( zWord[i] ){
644652
case '\\':
645
- xGet = thNextEscape; xSubst = thSubstEscape;
653
+ xGet = thNextEscape; xSubst = thSubstEscape;
646654
break;
647655
case '[':
648656
if( !interp->isListMode ){
649
- xGet = thNextCommand; xSubst = thSubstCommand;
657
+ xGet = thNextCommand; xSubst = thSubstCommand;
650658
break;
651659
}
652660
case '$':
653661
if( !interp->isListMode ){
654
- xGet = thNextVarname; xSubst = thSubstVarname;
662
+ xGet = thNextVarname; xSubst = thSubstVarname;
655663
break;
656664
}
657665
default: {
658666
thBufferWrite(interp, &output, &zWord[i], 1);
659667
continue; /* Go to the next iteration of the for(...) loop */
@@ -685,11 +693,11 @@
685693
** Return true if one of the following is true of the buffer pointed
686694
** to by zInput, length nInput:
687695
**
688696
** + It is empty, or
689697
** + It contains nothing but white-space, or
690
-** + It contains no non-white-space characters before the first
698
+** + It contains no non-white-space characters before the first
691699
** newline character.
692700
**
693701
** Otherwise return false.
694702
*/
695703
static int thEndOfLine(const char *zInput, int nInput){
@@ -725,16 +733,16 @@
725733
** // Free all memory allocated by Th_SplitList(). The arrays pointed
726734
** // to by argv and argl are invalidated by this call.
727735
** //
728736
** Th_Free(interp, argv);
729737
**
730
-*/
738
+*/
731739
static int thSplitList(
732740
Th_Interp *interp, /* Interpreter context */
733
- const char *zList, /* Pointer to buffer containing input list */
741
+ const char *zList, /* Pointer to buffer containing input list */
734742
int nList, /* Size of buffer pointed to by zList */
735
- char ***pazElem, /* OUT: Array of list elements */
743
+ char ***pazElem, /* OUT: Array of list elements */
736744
int **panElem, /* OUT: Lengths of each list element */
737745
int *pnCount /* OUT: Number of list elements */
738746
){
739747
int rc = TH_OK;
740748
@@ -774,14 +782,14 @@
774782
assert((lenbuf.nBuf/sizeof(int))==nCount);
775783
776784
assert((pazElem && panElem) || (!pazElem && !panElem));
777785
if( pazElem && rc==TH_OK ){
778786
int i;
779
- char *zElem;
787
+ char *zElem;
780788
int *anElem;
781789
char **azElem = Th_Malloc(interp,
782
- sizeof(char*) * nCount + /* azElem */
790
+ sizeof(char*) * nCount + /* azElem */
783791
sizeof(int) * nCount + /* anElem */
784792
strbuf.nBuf /* space for list element strings */
785793
);
786794
anElem = (int *)&azElem[nCount];
787795
zElem = (char *)&anElem[nCount];
@@ -795,11 +803,11 @@
795803
*panElem = anElem;
796804
}
797805
if( pnCount ){
798806
*pnCount = nCount;
799807
}
800
-
808
+
801809
finish:
802810
thBufferFree(interp, &strbuf);
803811
thBufferFree(interp, &lenbuf);
804812
return rc;
805813
}
@@ -876,18 +884,18 @@
876884
if( rc==TH_OK ){
877885
Th_Command *p = (Th_Command *)(pEntry->pData);
878886
const char **azArg = (const char **)argv;
879887
rc = p->xProc(interp, p->pContext, argc, azArg, argl);
880888
}
881
-
889
+
882890
/* If an error occurred, add this command to the stack trace report. */
883891
if( rc==TH_ERROR ){
884892
char *zRes;
885893
int nRes;
886894
char *zStack = 0;
887895
int nStack = 0;
888
-
896
+
889897
zRes = Th_TakeResult(interp, &nRes);
890898
if( TH_OK==Th_GetVar(interp, (char *)"::th_stack_trace", -1) ){
891899
zStack = Th_TakeResult(interp, &nStack);
892900
}
893901
Th_ListAppend(interp, &zStack, &nStack, zFirst, zInput-zFirst);
@@ -912,15 +920,15 @@
912920
**
913921
** Argument iFrame is interpreted as follows:
914922
**
915923
** * If iFrame is 0, this means the current frame.
916924
**
917
-** * If iFrame is negative, then the nth frame up the stack, where
918
-** n is the absolute value of iFrame. A value of -1 means the
925
+** * If iFrame is negative, then the nth frame up the stack, where
926
+** n is the absolute value of iFrame. A value of -1 means the
919927
** calling procedure.
920928
**
921
-** * If iFrame is +ve, then the nth frame from the bottom of the
929
+** * If iFrame is +ve, then the nth frame from the bottom of the
922930
** stack. An iFrame value of 1 means the toplevel (global) frame.
923931
*/
924932
static Th_Frame *getFrame(Th_Interp *interp, int iFrame){
925933
Th_Frame *p = interp->pFrame;
926934
int i;
@@ -948,28 +956,28 @@
948956
949957
950958
/*
951959
** Evaluate th1 script (zProgram, nProgram) in the frame identified by
952960
** argument iFrame. Leave either an error message or a result in the
953
-** interpreter result and return a th1 error code (TH_OK, TH_ERROR,
961
+** interpreter result and return a th1 error code (TH_OK, TH_ERROR,
954962
** TH_RETURN, TH_CONTINUE or TH_BREAK).
955963
*/
956964
int Th_Eval(Th_Interp *interp, int iFrame, const char *zProgram, int nProgram){
957965
int rc = TH_OK;
958966
Th_Frame *pSavedFrame = interp->pFrame;
959967
960
- /* Set Th_Interp.pFrame to the frame that this script is to be
968
+ /* Set Th_Interp.pFrame to the frame that this script is to be
961969
** evaluated in. The current frame is saved in pSavedFrame and will
962970
** be restored before this function returns.
963971
*/
964972
interp->pFrame = getFrame(interp, iFrame);
965973
966974
if( !interp->pFrame ){
967975
rc = TH_ERROR;
968976
}else{
969977
int nInput = nProgram;
970
-
978
+
971979
if( nInput<0 ){
972980
nInput = th_strlen(zProgram);
973981
}
974982
rc = thEvalLocal(interp, zProgram, nInput);
975983
}
@@ -995,13 +1003,13 @@
9951003
** array key name.
9961004
*/
9971005
static int thAnalyseVarname(
9981006
const char *zVarname,
9991007
int nVarname,
1000
- const char **pzOuter, /* OUT: Pointer to scalar/array name */
1008
+ const char **pzOuter, /* OUT: Pointer to scalar/array name */
10011009
int *pnOuter, /* OUT: Number of bytes at *pzOuter */
1002
- const char **pzInner, /* OUT: Pointer to array key (or null) */
1010
+ const char **pzInner, /* OUT: Pointer to array key (or null) */
10031011
int *pnInner, /* OUT: Number of bytes at *pzInner */
10041012
int *pisGlobal /* OUT: Set to true if this is a global ref */
10051013
){
10061014
const char *zOuter = zVarname;
10071015
int nOuter;
@@ -1042,13 +1050,28 @@
10421050
*pnInner = nInner;
10431051
*pisGlobal = isGlobal;
10441052
return TH_OK;
10451053
}
10461054
1055
+/*
1056
+** The Find structure is used to return extra information to callers of the
1057
+** thFindValue function. The fields within it are populated by thFindValue
1058
+** as soon as the necessary information is available. Callers should check
1059
+** each field of interest upon return.
1060
+*/
1061
+
1062
+struct Find {
1063
+ Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064
+ Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065
+ const char *zElem; /* Name of array element, if applicable */
1066
+ int nElem; /* Length of array element name, if applicable */
1067
+};
1068
+typedef struct Find Find;
1069
+
10471070
/*
10481071
** Input string (zVar, nVar) contains a variable name. This function locates
1049
-** the Th_Variable structure associated with the named variable. The
1072
+** the Th_Variable structure associated with the named variable. The
10501073
** variable name may be a global or local scalar or array variable
10511074
**
10521075
** If the create argument is non-zero and the named variable does not exist
10531076
** it is created. Otherwise, an error is left in the interpreter result
10541077
** and NULL returned.
@@ -1055,16 +1078,19 @@
10551078
**
10561079
** If the arrayok argument is false and the named variable is an array,
10571080
** an error is left in the interpreter result and NULL returned. If
10581081
** arrayok is true an array name is Ok.
10591082
*/
1083
+
10601084
static Th_Variable *thFindValue(
10611085
Th_Interp *interp,
1062
- const char *zVar, /* Pointer to variable name */
1063
- int nVar, /* Number of bytes at nVar */
1064
- int create, /* If true, create the variable if not found */
1065
- int arrayok /* If true, an array is Ok. Otherwise array==error */
1086
+ const char *zVar, /* Pointer to variable name */
1087
+ int nVar, /* Number of bytes at nVar */
1088
+ int create, /* If true, create the variable if not found */
1089
+ int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090
+ int noerror, /* If false, set interpreter result to error */
1091
+ Find *pFind /* If non-zero, place output here */
10661092
){
10671093
const char *zOuter;
10681094
int nOuter;
10691095
const char *zInner;
10701096
int nInner;
@@ -1073,16 +1099,24 @@
10731099
Th_HashEntry *pEntry;
10741100
Th_Frame *pFrame = interp->pFrame;
10751101
Th_Variable *pValue;
10761102
10771103
thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104
+ if( pFind ){
1105
+ memset(pFind, 0, sizeof(Find));
1106
+ pFind->zElem = zInner;
1107
+ pFind->nElem = nInner;
1108
+ }
10781109
if( isGlobal ){
10791110
while( pFrame->pCaller ) pFrame = pFrame->pCaller;
10801111
}
10811112
10821113
pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1083
- assert(pEntry || !create);
1114
+ assert(pEntry || create<=0);
1115
+ if( pFind ){
1116
+ pFind->pValueEntry = pEntry;
1117
+ }
10841118
if( !pEntry ){
10851119
goto no_such_var;
10861120
}
10871121
10881122
pValue = (Th_Variable *)pEntry->pData;
@@ -1093,20 +1127,26 @@
10931127
pEntry->pData = (void *)pValue;
10941128
}
10951129
10961130
if( zInner ){
10971131
if( pValue->zData ){
1098
- Th_ErrorMessage(interp, "variable is a scalar:", zOuter, nOuter);
1132
+ if( !noerror ){
1133
+ Th_ErrorMessage(interp, "variable is a scalar:", zOuter, nOuter);
1134
+ }
10991135
return 0;
11001136
}
11011137
if( !pValue->pHash ){
11021138
if( !create ){
11031139
goto no_such_var;
11041140
}
11051141
pValue->pHash = Th_HashNew(interp);
11061142
}
11071143
pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144
+ assert(pEntry || create<=0);
1145
+ if( pFind ){
1146
+ pFind->pElemEntry = pEntry;
1147
+ }
11081148
if( !pEntry ){
11091149
goto no_such_var;
11101150
}
11111151
pValue = (Th_Variable *)pEntry->pData;
11121152
if( !pValue ){
@@ -1115,34 +1155,38 @@
11151155
pValue->nRef = 1;
11161156
pEntry->pData = (void *)pValue;
11171157
}
11181158
}else{
11191159
if( pValue->pHash && !arrayok ){
1120
- Th_ErrorMessage(interp, "variable is an array:", zOuter, nOuter);
1160
+ if( !noerror ){
1161
+ Th_ErrorMessage(interp, "variable is an array:", zOuter, nOuter);
1162
+ }
11211163
return 0;
11221164
}
11231165
}
11241166
11251167
return pValue;
11261168
11271169
no_such_var:
1128
- Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1170
+ if( !noerror ){
1171
+ Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1172
+ }
11291173
return 0;
11301174
}
11311175
11321176
/*
1133
-** String (zVar, nVar) must contain the name of a scalar variable or
1134
-** array member. Look up the variable, store its current value in
1177
+** String (zVar, nVar) must contain the name of a scalar variable or
1178
+** array member. Look up the variable, store its current value in
11351179
** the interpreter result and return TH_OK.
11361180
**
11371181
** If the named variable does not exist, return TH_ERROR and leave
11381182
** an error message in the interpreter result.
11391183
*/
11401184
int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
11411185
Th_Variable *pValue;
11421186
1143
- pValue = thFindValue(interp, zVar, nVar, 0, 0);
1187
+ pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
11441188
if( !pValue ){
11451189
return TH_ERROR;
11461190
}
11471191
if( !pValue->zData ){
11481192
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1154,11 +1198,12 @@
11541198
11551199
/*
11561200
** Return true if variable (zVar, nVar) exists.
11571201
*/
11581202
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1159
- return thFindValue(interp, zVar, nVar, 0, 0)!=0;
1203
+ Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204
+ return pValue && (pValue->zData || pValue->pHash);
11601205
}
11611206
11621207
/*
11631208
** String (zVar, nVar) must contain the name of a scalar variable or
11641209
** array member. If the variable does not exist it is created. The
@@ -1166,19 +1211,19 @@
11661211
**
11671212
** If (zVar, nVar) refers to an existing array, TH_ERROR is returned
11681213
** and an error message left in the interpreter result.
11691214
*/
11701215
int Th_SetVar(
1171
- Th_Interp *interp,
1172
- const char *zVar,
1216
+ Th_Interp *interp,
1217
+ const char *zVar,
11731218
int nVar,
11741219
const char *zValue,
11751220
int nValue
11761221
){
11771222
Th_Variable *pValue;
11781223
1179
- pValue = thFindValue(interp, zVar, nVar, 1, 0);
1224
+ pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
11801225
if( !pValue ){
11811226
return TH_ERROR;
11821227
}
11831228
11841229
if( nValue<0 ){
@@ -1202,13 +1247,13 @@
12021247
** Create a variable link so that accessing variable (zLocal, nLocal) is
12031248
** the same as accessing variable (zLink, nLink) in stack frame iFrame.
12041249
*/
12051250
int Th_LinkVar(
12061251
Th_Interp *interp, /* Interpreter */
1207
- const char *zLocal, int nLocal, /* Local varname */
1252
+ const char *zLocal, int nLocal, /* Local varname */
12081253
int iFrame, /* Stack frame of linked var */
1209
- const char *zLink, int nLink /* Linked varname */
1254
+ const char *zLink, int nLink /* Linked varname */
12101255
){
12111256
Th_Frame *pSavedFrame = interp->pFrame;
12121257
Th_Frame *pFrame;
12131258
Th_HashEntry *pEntry;
12141259
Th_Variable *pValue;
@@ -1217,11 +1262,11 @@
12171262
if( !pFrame ){
12181263
return TH_ERROR;
12191264
}
12201265
pSavedFrame = interp->pFrame;
12211266
interp->pFrame = pFrame;
1222
- pValue = thFindValue(interp, zLink, nLink, 1, 1);
1267
+ pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
12231268
interp->pFrame = pSavedFrame;
12241269
12251270
pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
12261271
if( pEntry->pData ){
12271272
Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1238,25 +1283,68 @@
12381283
** an array, or an array member. If the identified variable exists, it
12391284
** is deleted and TH_OK returned. Otherwise, an error message is left
12401285
** in the interpreter result and TH_ERROR is returned.
12411286
*/
12421287
int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288
+ Find find;
12431289
Th_Variable *pValue;
1290
+ Th_HashEntry *pEntry;
1291
+ int rc = TH_ERROR;
12441292
1245
- pValue = thFindValue(interp, zVar, nVar, 1, 1);
1293
+ pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
12461294
if( !pValue ){
1247
- return TH_ERROR;
1248
- }
1249
-
1250
- Th_Free(interp, pValue->zData);
1251
- pValue->zData = 0;
1252
- if( pValue->pHash ){
1253
- Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1254
- Th_HashDelete(interp, pValue->pHash);
1255
- pValue->pHash = 0;
1256
- }
1257
- return TH_OK;
1295
+ return rc;
1296
+ }
1297
+
1298
+ if( pValue->zData || pValue->pHash ){
1299
+ rc = TH_OK;
1300
+ }else {
1301
+ Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302
+ }
1303
+
1304
+ /*
1305
+ ** The variable may be shared by more than one frame; therefore, make sure
1306
+ ** it is actually freed prior to freeing the parent structure. The values
1307
+ ** for the variable must be freed now so the variable appears undefined in
1308
+ ** all frames. The hash entry in the current frame must also be deleted
1309
+ ** now; otherwise, if the current stack frame is later popped, it will try
1310
+ ** to delete a variable which has already been freed.
1311
+ */
1312
+ if( find.zElem ){
1313
+ pEntry = find.pElemEntry;
1314
+ }else{
1315
+ pEntry = find.pValueEntry;
1316
+ }
1317
+ assert( pEntry );
1318
+ assert( pValue );
1319
+ if( thFreeVariable(pEntry, (void *)interp) ){
1320
+ if( find.zElem ){
1321
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1322
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323
+ }else if( pEntry->pData ){
1324
+ Th_Free(interp, pEntry->pData);
1325
+ pEntry->pData = 0;
1326
+ }
1327
+ }else{
1328
+ if( pValue->zData ){
1329
+ Th_Free(interp, pValue->zData);
1330
+ pValue->zData = 0;
1331
+ }
1332
+ if( pValue->pHash ){
1333
+ Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334
+ Th_HashDelete(interp, pValue->pHash);
1335
+ pValue->pHash = 0;
1336
+ }
1337
+ if( find.zElem ){
1338
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1339
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340
+ }
1341
+ }
1342
+ if( !find.zElem ){
1343
+ Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344
+ }
1345
+ return rc;
12581346
}
12591347
12601348
/*
12611349
** Return an allocated buffer containing a copy of string (z, n). The
12621350
** caller is responsible for eventually calling Th_Free() to free
@@ -1291,11 +1379,11 @@
12911379
if( interp ){
12921380
char *zRes = 0;
12931381
int nRes = 0;
12941382
12951383
Th_SetVar(interp, (char *)"::th_stack_trace", -1, 0, 0);
1296
-
1384
+
12971385
Th_StringAppend(interp, &zRes, &nRes, zPre, -1);
12981386
if( zRes[nRes-1]=='"' ){
12991387
Th_StringAppend(interp, &zRes, &nRes, z, n);
13001388
Th_StringAppend(interp, &zRes, &nRes, (const char *)"\"", 1);
13011389
}else{
@@ -1373,12 +1461,12 @@
13731461
return (char *)Th_Malloc(pInterp, 1);
13741462
}
13751463
}
13761464
13771465
1378
-/*
1379
-** Wrappers around the supplied malloc() and free()
1466
+/*
1467
+** Wrappers around the supplied malloc() and free()
13801468
*/
13811469
void *Th_Malloc(Th_Interp *pInterp, int nByte){
13821470
void *p = pInterp->pVtab->xMalloc(nByte);
13831471
if( p ){
13841472
memset(p, 0, nByte);
@@ -1390,16 +1478,16 @@
13901478
pInterp->pVtab->xFree(z);
13911479
}
13921480
}
13931481
13941482
/*
1395
-** Install a new th1 command.
1483
+** Install a new th1 command.
13961484
**
13971485
** If a command of the same name already exists, it is deleted automatically.
13981486
*/
13991487
int Th_CreateCommand(
1400
- Th_Interp *interp,
1488
+ Th_Interp *interp,
14011489
const char *zName, /* New command name */
14021490
Th_CommandProc xProc, /* Command callback proc */
14031491
void *pContext, /* Value to pass as second arg to xProc */
14041492
void (*xDel)(Th_Interp *, void *) /* Command destructor callback */
14051493
){
@@ -1417,27 +1505,27 @@
14171505
}
14181506
pCommand->xProc = xProc;
14191507
pCommand->pContext = pContext;
14201508
pCommand->xDel = xDel;
14211509
pEntry->pData = (void *)pCommand;
1422
-
1510
+
14231511
return TH_OK;
14241512
}
14251513
14261514
/*
1427
-** Rename the existing command (zName, nName) to (zNew, nNew). If nNew is 0,
1515
+** Rename the existing command (zName, nName) to (zNew, nNew). If nNew is 0,
14281516
** the command is deleted instead of renamed.
14291517
**
14301518
** If successful, TH_OK is returned. If command zName does not exist, or
1431
-** if command zNew already exists, an error message is left in the
1519
+** if command zNew already exists, an error message is left in the
14321520
** interpreter result and TH_ERROR is returned.
14331521
*/
14341522
int Th_RenameCommand(
1435
- Th_Interp *interp,
1436
- const char *zName, /* Existing command name */
1523
+ Th_Interp *interp,
1524
+ const char *zName, /* Existing command name */
14371525
int nName, /* Number of bytes at zName */
1438
- const char *zNew, /* New command name */
1526
+ const char *zNew, /* New command name */
14391527
int nNew /* Number of bytes at zNew */
14401528
){
14411529
Th_HashEntry *pEntry;
14421530
Th_HashEntry *pNewEntry;
14431531
@@ -1491,11 +1579,11 @@
14911579
** If an error occurs (if (zList, nList) is not a valid list) an error
14921580
** message is left in the interpreter result and TH_ERROR returned.
14931581
**
14941582
** If successful, *pnCount is set to the number of elements in the list.
14951583
** panElem is set to point at an array of *pnCount integers - the lengths
1496
-** of the element values. *pazElem is set to point at an array of
1584
+** of the element values. *pazElem is set to point at an array of
14971585
** pointers to buffers containing the array element's data.
14981586
**
14991587
** To free the arrays allocated at *pazElem and *panElem, the caller
15001588
** should call Th_Free() on *pazElem only. Exactly one such call to
15011589
** Th_Free() must be made per call to Th_SplitList().
@@ -1517,13 +1605,13 @@
15171605
** Th_Free(interp, azElem);
15181606
**
15191607
*/
15201608
int Th_SplitList(
15211609
Th_Interp *interp,
1522
- const char *zList, /* Pointer to buffer containing list */
1610
+ const char *zList, /* Pointer to buffer containing list */
15231611
int nList, /* Number of bytes at zList */
1524
- char ***pazElem, /* OUT: Array of pointers to element data */
1612
+ char ***pazElem, /* OUT: Array of pointers to element data */
15251613
int **panElem, /* OUT: Array of element data lengths */
15261614
int *pnCount /* OUT: Number of elements in list */
15271615
){
15281616
int rc;
15291617
interp->isListMode = 1;
@@ -1534,16 +1622,16 @@
15341622
}
15351623
return rc;
15361624
}
15371625
15381626
/*
1539
-** Append a new element to an existing th1 list. The element to append
1627
+** Append a new element to an existing th1 list. The element to append
15401628
** to the list is (zElem, nElem).
15411629
**
15421630
** A pointer to the existing list must be stored at *pzList when this
1543
-** function is called. The length must be stored in *pnList. The value
1544
-** of *pzList must either be NULL (in which case *pnList must be 0), or
1631
+** function is called. The length must be stored in *pnList. The value
1632
+** of *pzList must either be NULL (in which case *pnList must be 0), or
15451633
** a pointer to memory obtained from Th_Malloc().
15461634
**
15471635
** This function calls Th_Free() to free the buffer at *pzList and sets
15481636
** *pzList to point to a new buffer containing the new list value. *pnList
15491637
** is similarly updated before returning. The return value is always TH_OK.
@@ -1560,13 +1648,13 @@
15601648
** Th_Free(interp, zList);
15611649
**
15621650
*/
15631651
int Th_ListAppend(
15641652
Th_Interp *interp, /* Interpreter context */
1565
- char **pzList, /* IN/OUT: Ptr to ptr to list */
1653
+ char **pzList, /* IN/OUT: Ptr to ptr to list */
15661654
int *pnList, /* IN/OUT: Current length of *pzList */
1567
- const char *zElem, /* Data to append */
1655
+ const char *zElem, /* Data to append */
15681656
int nElem /* Length of nElem */
15691657
){
15701658
Buffer output;
15711659
int i;
15721660
@@ -1615,13 +1703,13 @@
16151703
** Append a new element to an existing th1 string. This function uses
16161704
** the same interface as the Th_ListAppend() function.
16171705
*/
16181706
int Th_StringAppend(
16191707
Th_Interp *interp, /* Interpreter context */
1620
- char **pzStr, /* IN/OUT: Ptr to ptr to list */
1708
+ char **pzStr, /* IN/OUT: Ptr to ptr to list */
16211709
int *pnStr, /* IN/OUT: Current length of *pzStr */
1622
- const char *zElem, /* Data to append */
1710
+ const char *zElem, /* Data to append */
16231711
int nElem /* Length of nElem */
16241712
){
16251713
char *zNew;
16261714
int nNew;
16271715
@@ -1639,11 +1727,11 @@
16391727
*pnStr = nNew;
16401728
16411729
return TH_OK;
16421730
}
16431731
1644
-/*
1732
+/*
16451733
** Delete an interpreter.
16461734
*/
16471735
void Th_DeleteInterp(Th_Interp *interp){
16481736
assert(interp->pFrame);
16491737
assert(0==interp->pFrame->pCaller);
@@ -1660,11 +1748,11 @@
16601748
16611749
/* Delete the interpreter structure itself. */
16621750
Th_Free(interp, (void *)interp);
16631751
}
16641752
1665
-/*
1753
+/*
16661754
** Create a new interpreter.
16671755
*/
16681756
Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){
16691757
Th_Interp *p;
16701758
@@ -1694,11 +1782,11 @@
16941782
Operator *pOp;
16951783
Expr *pParent;
16961784
Expr *pLeft;
16971785
Expr *pRight;
16981786
1699
- char *zValue; /* Pointer to literal value */
1787
+ char *zValue; /* Pointer to literal value */
17001788
int nValue; /* Length of literal value buffer */
17011789
};
17021790
17031791
/* Unary operators */
17041792
#define OP_UNARY_MINUS 2
@@ -1750,11 +1838,11 @@
17501838
{"+", OP_UNARY_PLUS, 1, ARG_NUMBER},
17511839
{"~", OP_BITWISE_NOT, 1, ARG_INTEGER},
17521840
{"!", OP_LOGICAL_NOT, 1, ARG_INTEGER},
17531841
17541842
/* Binary operators. It is important to the parsing in Th_Expr() that
1755
- * the two-character symbols ("==") appear before the one-character
1843
+ * the two-character symbols ("==") appear before the one-character
17561844
* ones ("="). And that the priorities of all binary operators are
17571845
* integers between 2 and 12.
17581846
*/
17591847
{"<<", OP_LEFTSHIFT, 4, ARG_INTEGER},
17601848
{">>", OP_RIGHTSHIFT, 4, ARG_INTEGER},
@@ -1781,16 +1869,16 @@
17811869
{0,0,0,0}
17821870
};
17831871
17841872
/*
17851873
** The first part of the string (zInput,nInput) contains a number.
1786
-** Set *pnVarname to the number of bytes in the numeric string.
1874
+** Set *pnVarname to the number of bytes in the numeric string.
17871875
*/
17881876
static int thNextNumber(
1789
- Th_Interp *interp,
1790
- const char *zInput,
1791
- int nInput,
1877
+ Th_Interp *interp,
1878
+ const char *zInput,
1879
+ int nInput,
17921880
int *pnLiteral
17931881
){
17941882
int i;
17951883
int seenDot = 0;
17961884
for(i=0; i<nInput; i++){
@@ -1856,11 +1944,11 @@
18561944
if( eArgType==ARG_NUMBER ){
18571945
if( (zLeft==0 || TH_OK==Th_ToInt(0, zLeft, nLeft, &iLeft))
18581946
&& (zRight==0 || TH_OK==Th_ToInt(0, zRight, nRight, &iRight))
18591947
){
18601948
eArgType = ARG_INTEGER;
1861
- }else if(
1949
+ }else if(
18621950
(zLeft && TH_OK!=Th_ToDouble(interp, zLeft, nLeft, &fLeft)) ||
18631951
(zRight && TH_OK!=Th_ToDouble(interp, zRight, nRight, &fRight))
18641952
){
18651953
/* A type error. */
18661954
rc = TH_ERROR;
@@ -1868,28 +1956,30 @@
18681956
}else if( eArgType==ARG_INTEGER ){
18691957
rc = Th_ToInt(interp, zLeft, nLeft, &iLeft);
18701958
if( rc==TH_OK && zRight ){
18711959
rc = Th_ToInt(interp, zRight, nRight, &iRight);
18721960
}
1873
- }
1961
+ }
18741962
}
18751963
18761964
if( rc==TH_OK && eArgType==ARG_INTEGER ){
18771965
int iRes = 0;
18781966
switch( pExpr->pOp->eOp ) {
18791967
case OP_MULTIPLY: iRes = iLeft*iRight; break;
18801968
case OP_DIVIDE:
1881
- if(!iRight){
1969
+ if( !iRight ){
18821970
Th_ErrorMessage(interp, "Divide by 0:", zLeft, nLeft);
1883
- return TH_ERROR;
1971
+ rc = TH_ERROR;
1972
+ goto finish;
18841973
}
18851974
iRes = iLeft/iRight;
18861975
break;
18871976
case OP_MODULUS:
1888
- if(!iRight){
1977
+ if( !iRight ){
18891978
Th_ErrorMessage(interp, "Modulo by 0:", zLeft, nLeft);
1890
- return TH_ERROR;
1979
+ rc = TH_ERROR;
1980
+ goto finish;
18911981
}
18921982
iRes = iLeft%iRight;
18931983
break;
18941984
case OP_ADD: iRes = iLeft+iRight; break;
18951985
case OP_SUBTRACT: iRes = iLeft-iRight; break;
@@ -1913,11 +2003,18 @@
19132003
}
19142004
Th_SetResultInt(interp, iRes);
19152005
}else if( rc==TH_OK && eArgType==ARG_NUMBER ){
19162006
switch( pExpr->pOp->eOp ) {
19172007
case OP_MULTIPLY: Th_SetResultDouble(interp, fLeft*fRight); break;
1918
- case OP_DIVIDE: Th_SetResultDouble(interp, fLeft/fRight); break;
2008
+ case OP_DIVIDE:
2009
+ if( fRight==0.0 ){
2010
+ Th_ErrorMessage(interp, "Divide by 0:", zLeft, nLeft);
2011
+ rc = TH_ERROR;
2012
+ goto finish;
2013
+ }
2014
+ Th_SetResultDouble(interp, fLeft/fRight);
2015
+ break;
19192016
case OP_ADD: Th_SetResultDouble(interp, fLeft+fRight); break;
19202017
case OP_SUBTRACT: Th_SetResultDouble(interp, fLeft-fRight); break;
19212018
case OP_LT: Th_SetResultInt(interp, fLeft<fRight); break;
19222019
case OP_GT: Th_SetResultInt(interp, fLeft>fRight); break;
19232020
case OP_LE: Th_SetResultInt(interp, fLeft<=fRight); break;
@@ -1936,10 +2033,12 @@
19362033
case OP_SEQ: Th_SetResultInt(interp, iEqual); break;
19372034
case OP_SNE: Th_SetResultInt(interp, !iEqual); break;
19382035
default: assert(!"Internal error");
19392036
}
19402037
}
2038
+
2039
+ finish:
19412040
19422041
Th_Free(interp, zLeft);
19432042
Th_Free(interp, zRight);
19442043
}
19452044
@@ -1959,11 +2058,11 @@
19592058
#define ISTERM(x) (apToken[x] && (!apToken[x]->pOp || apToken[x]->pLeft))
19602059
19612060
for(jj=0; jj<nToken; jj++){
19622061
if( apToken[jj]->pOp && apToken[jj]->pOp->eOp==OP_OPEN_BRACKET ){
19632062
int nNest = 1;
1964
- int iLeft = jj;
2063
+ int iLeft = jj;
19652064
19662065
for(jj++; jj<nToken; jj++){
19672066
Operator *pOp = apToken[jj]->pOp;
19682067
if( pOp && pOp->eOp==OP_OPEN_BRACKET ) nNest++;
19692068
if( pOp && pOp->eOp==OP_CLOSE_BRACKET ) nNest--;
@@ -2033,11 +2132,11 @@
20332132
/*
20342133
** Parse a string containing a TH expression to a list of tokens.
20352134
*/
20362135
static int exprParse(
20372136
Th_Interp *interp, /* Interpreter to leave error message in */
2038
- const char *zExpr, /* Pointer to input string */
2137
+ const char *zExpr, /* Pointer to input string */
20392138
int nExpr, /* Number of bytes at zExpr */
20402139
Expr ***papToken, /* OUT: Array of tokens. */
20412140
int *pnToken /* OUT: Size of token array */
20422141
){
20432142
int i;
@@ -2108,11 +2207,11 @@
21082207
memcpy(pNew->zValue, z, pNew->nValue);
21092208
i += pNew->nValue;
21102209
}
21112210
if( (nToken%16)==0 ){
21122211
/* Grow the apToken array. */
2113
- Expr **apTokenOld = apToken;
2212
+ Expr **apTokenOld = apToken;
21142213
apToken = Th_Malloc(interp, sizeof(Expr *)*(nToken+16));
21152214
memcpy(apToken, apTokenOld, sizeof(Expr *)*nToken);
21162215
}
21172216
21182217
/* Put the new token at the end of the apToken array */
@@ -2133,11 +2232,11 @@
21332232
/*
21342233
** Evaluate the string (zExpr, nExpr) as a Th expression. Store
21352234
** the result in the interpreter interp and return TH_OK if
21362235
** successful. If an error occurs, store an error message in
21372236
** the interpreter result and return an error code.
2138
-*/
2237
+*/
21392238
int Th_Expr(Th_Interp *interp, const char *zExpr, int nExpr){
21402239
int rc; /* Return Code */
21412240
int i; /* Loop counter */
21422241
21432242
int nToken = 0;
@@ -2150,11 +2249,11 @@
21502249
/* Parse the expression to a list of tokens. */
21512250
rc = exprParse(interp, zExpr, nExpr, &apToken, &nToken);
21522251
21532252
/* If the parsing was successful, create an expression tree from
21542253
** the parsed list of tokens. If successful, apToken[0] is set
2155
- ** to point to the root of the expression tree.
2254
+ ** to point to the root of the expression tree.
21562255
*/
21572256
if( rc==TH_OK ){
21582257
rc = exprMakeTree(interp, apToken, nToken);
21592258
}
21602259
@@ -2188,16 +2287,17 @@
21882287
21892288
/*
21902289
** Iterate through all values currently stored in the hash table. Invoke
21912290
** the callback function xCallback for each entry. The second argument
21922291
** passed to xCallback is a copy of the fourth argument passed to this
2193
-** function.
2292
+** function. The return value from the callback function xCallback is
2293
+** ignored.
21942294
*/
21952295
void Th_HashIterate(
2196
- Th_Interp *interp,
2296
+ Th_Interp *interp,
21972297
Th_Hash *pHash,
2198
- void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2298
+ int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
21992299
void *pContext
22002300
){
22012301
int i;
22022302
for(i=0; i<TH_HASHSIZE; i++){
22032303
Th_HashEntry *pEntry;
@@ -2208,14 +2308,15 @@
22082308
}
22092309
}
22102310
}
22112311
22122312
/*
2213
-** Helper function for Th_HashDelete().
2313
+** Helper function for Th_HashDelete(). Always returns non-zero.
22142314
*/
2215
-static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2315
+static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
22162316
Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317
+ return 1;
22172318
}
22182319
22192320
/*
22202321
** Free a hash-table previously allocated by Th_HashNew().
22212322
*/
@@ -2225,14 +2326,14 @@
22252326
Th_Free(interp, pHash);
22262327
}
22272328
}
22282329
22292330
/*
2230
-** This function is used to insert or delete hash table items, or to
2331
+** This function is used to insert or delete hash table items, or to
22312332
** query a hash table for an existing item.
22322333
**
2233
-** If parameter op is less than zero, then the hash-table element
2334
+** If parameter op is less than zero, then the hash-table element
22342335
** identified by (zKey, nKey) is removed from the hash-table if it
22352336
** exists. NULL is returned.
22362337
**
22372338
** Otherwise, if the hash-table contains an item with key (zKey, nKey),
22382339
** a pointer to the associated Th_HashEntry is returned. If parameter
@@ -2239,11 +2340,11 @@
22392340
** op is greater than zero, then a new entry is added if one cannot
22402341
** be found. If op is zero, then NULL is returned if the item is
22412342
** not already present in the hash-table.
22422343
*/
22432344
Th_HashEntry *Th_HashFind(
2244
- Th_Interp *interp,
2345
+ Th_Interp *interp,
22452346
Th_Hash *pHash,
22462347
const char *zKey,
22472348
int nKey,
22482349
int op /* -ve = delete, 0 = find, +ve = insert */
22492350
){
@@ -2307,11 +2408,11 @@
23072408
** '\f' 0x0C
23082409
** '\r' 0x0D
23092410
**
23102411
** Whitespace characters have the 0x01 flag set. Decimal digits have the
23112412
** 0x2 flag set. Single byte printable characters have the 0x4 flag set.
2312
-** Alphabet characters have the 0x8 bit set.
2413
+** Alphabet characters have the 0x8 bit set.
23132414
**
23142415
** The special list characters have the 0x10 flag set
23152416
**
23162417
** { } [ ] \ ; ' "
23172418
**
@@ -2458,14 +2559,14 @@
24582559
return z - zBegin;
24592560
}
24602561
24612562
/*
24622563
** Try to convert the string passed as arguments (z, n) to an integer.
2463
-** If successful, store the result in *piOut and return TH_OK.
2564
+** If successful, store the result in *piOut and return TH_OK.
24642565
**
2465
-** If the string cannot be converted to an integer, return TH_ERROR.
2466
-** If the interp argument is not NULL, leave an error message in the
2566
+** If the string cannot be converted to an integer, return TH_ERROR.
2567
+** If the interp argument is not NULL, leave an error message in the
24672568
** interpreter result too.
24682569
*/
24692570
int Th_ToInt(Th_Interp *interp, const char *z, int n, int *piOut){
24702571
int i = 0;
24712572
int iOut = 0;
@@ -2493,20 +2594,20 @@
24932594
return TH_OK;
24942595
}
24952596
24962597
/*
24972598
** Try to convert the string passed as arguments (z, n) to a double.
2498
-** If successful, store the result in *pfOut and return TH_OK.
2599
+** If successful, store the result in *pfOut and return TH_OK.
24992600
**
2500
-** If the string cannot be converted to a double, return TH_ERROR.
2501
-** If the interp argument is not NULL, leave an error message in the
2601
+** If the string cannot be converted to a double, return TH_ERROR.
2602
+** If the interp argument is not NULL, leave an error message in the
25022603
** interpreter result too.
25032604
*/
25042605
int Th_ToDouble(
2505
- Th_Interp *interp,
2506
- const char *z,
2507
- int n,
2606
+ Th_Interp *interp,
2607
+ const char *z,
2608
+ int n,
25082609
double *pfOut
25092610
){
25102611
if( !sqlite3IsNumber((const char *)z, 0) ){
25112612
Th_ErrorMessage(interp, "expected number, got: \"", z, n);
25122613
return TH_ERROR;
@@ -2547,33 +2648,33 @@
25472648
** the double fVal and return TH_OK.
25482649
*/
25492650
int Th_SetResultDouble(Th_Interp *interp, double fVal){
25502651
int i; /* Iterator variable */
25512652
double v = fVal; /* Input value */
2552
- char zBuf[128]; /* Output buffer */
2553
- char *z = zBuf; /* Output cursor */
2653
+ char zBuf[128]; /* Output buffer */
2654
+ char *z = zBuf; /* Output cursor */
25542655
int iDot = 0; /* Digit after which to place decimal point */
25552656
int iExp = 0; /* Exponent (NN in eNN) */
2556
- const char *zExp; /* String representation of iExp */
2657
+ const char *zExp; /* String representation of iExp */
25572658
25582659
/* Precision: */
25592660
#define INSIGNIFICANT 0.000000000001
25602661
#define ROUNDER 0.0000000000005
25612662
double insignificant = INSIGNIFICANT;
25622663
25632664
/* If the real value is negative, write a '-' character to the
25642665
* output and transform v to the corresponding positive number.
2565
- */
2666
+ */
25662667
if( v<0.0 ){
25672668
*z++ = '-';
25682669
v *= -1.0;
25692670
}
25702671
2571
- /* Normalize v to a value between 1.0 and 10.0. Integer
2672
+ /* Normalize v to a value between 1.0 and 10.0. Integer
25722673
* variable iExp is set to the exponent. i.e the original
25732674
* value is (v * 10^iExp) (or the negative thereof).
2574
- */
2675
+ */
25752676
if( v>0.0 ){
25762677
while( (v+ROUNDER)>=10.0 ) { iExp++; v *= 0.1; }
25772678
while( (v+ROUNDER)<1.0 ) { iExp--; v *= 10.0; }
25782679
}
25792680
v += ROUNDER;
25802681
--- src/th.c
+++ src/th.c
@@ -1,8 +1,8 @@
1
2 /*
3 ** The implementation of the TH core. This file contains the parser, and
4 ** the implementation of the interface in th.h.
5 */
6
7 #include "config.h"
8 #include "th.h"
@@ -16,11 +16,11 @@
16 /*
17 ** Interpreter structure.
18 */
19 struct Th_Interp {
20 Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */
21 char *zResult; /* Current interpreter result (Th_Malloc()ed) */
22 int nResult; /* number of bytes in zResult */
23 Th_Hash *paCmd; /* Table of registered commands */
24 Th_Frame *pFrame; /* Current execution frame */
25 int isListMode; /* True if thSplitList() should operate in "list" mode */
26 };
@@ -42,25 +42,25 @@
42 ** are stored in the Th_Frame.paVar hash table member of the associated
43 ** stack frame object.
44 **
45 ** When an interpreter is created, a single Th_Frame structure is also
46 ** allocated - the global variable scope. Th_Interp.pFrame (the current
47 ** interpreter frame) is initialised to point to this Th_Frame. It is
48 ** not deleted for the lifetime of the interpreter (because the global
49 ** frame never goes out of scope).
50 **
51 ** New stack frames are created by the Th_InFrame() function. Before
52 ** invoking its callback function, Th_InFrame() allocates a new Th_Frame
53 ** structure with pCaller set to the current frame (Th_Interp.pFrame),
54 ** and sets the current frame to the new frame object. After the callback
55 ** has been invoked, the allocated Th_Frame is deleted and the value
56 ** of the current frame pointer restored.
57 **
58 ** By default, the Th_SetVar(), Th_UnsetVar() and Th_GetVar() functions
59 ** access variable values in the current frame. If they need to access
60 ** the global frame, they do so by traversing the pCaller pointer list.
61 ** Likewise, the Th_LinkVar() function uses the pCaller pointers to
62 ** link to variables located in the global or other stack frames.
63 */
64 struct Th_Frame {
65 Th_Hash *paVar; /* Variables defined in this scope */
66 Th_Frame *pCaller; /* Calling frame */
@@ -84,11 +84,11 @@
84 ** value.
85 */
86 struct Th_Variable {
87 int nRef; /* Number of references to this structure */
88 int nData; /* Number of bytes at Th_Variable.zData */
89 char *zData; /* Data for scalar variables */
90 Th_Hash *pHash; /* Data for array variables */
91 };
92
93 /*
94 ** Hash table API:
@@ -105,24 +105,24 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static void thFreeVariable(Th_HashEntry*, void*);
111 static void thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
117 ** sequence etc.), these functions determine the number of bytes
118 ** of the input consumed by the construct. For example:
119 **
120 ** int nByte;
121 ** thNextCommand(interp, "[expr $a+1] $nIter", 18, &nByte);
122 **
123 ** results in variable nByte being set to 11. Or,
124 **
125 ** thNextVarname(interp, "$a+1", 4, &nByte);
126 **
127 ** results in nByte being set to 2.
128 */
@@ -132,24 +132,24 @@
132 static int thNextNumber (Th_Interp*, const char *z, int n, int *pN);
133 static int thNextSpace (Th_Interp*, const char *z, int n, int *pN);
134
135 /*
136 ** Given that the input string (z, n) contains a language construct of
137 ** the relevant type (a command enclosed in [], an escape sequence
138 ** like "\xFF" or a variable reference like "${varname}", perform
139 ** substitution on the string and store the resulting string in
140 ** the interpreter result.
141 */
142 static int thSubstCommand(Th_Interp*, const char *z, int n);
143 static int thSubstEscape (Th_Interp*, const char *z, int n);
144 static int thSubstVarname(Th_Interp*, const char *z, int n);
145
146 /*
147 ** Given that there is a th1 word located at the start of the input
148 ** string (z, n), determine the length in bytes of that word. If the
149 ** isCmd argument is non-zero, then an unescaped ";" byte not
150 ** located inside of a block or quoted string is considered to mark
151 ** the end of the word.
152 */
153 static int thNextWord(Th_Interp*, const char *z, int n, int *pN, int isCmd);
154
155 /*
@@ -176,13 +176,13 @@
176 ** Append nAdd bytes of content copied from zAdd to the end of buffer
177 ** pBuffer. If there is not enough space currently allocated, resize
178 ** the allocation to make space.
179 */
180 static int thBufferWrite(
181 Th_Interp *interp,
182 Buffer *pBuffer,
183 const char *zAdd,
184 int nAdd
185 ){
186 int nReq;
187
188 if( nAdd<0 ){
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
 
 
263 */
264 static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266 pValue->nRef--;
267 assert( pValue->nRef>=0 );
268 if( pValue->nRef==0 ){
269 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271 if( pValue->pHash ){
272 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273 Th_HashDelete(interp, pValue->pHash);
274 }
275 Th_Free(interp, pValue);
 
 
276 }
 
277 }
278
279 /*
280 ** Argument pEntry points to an entry in the command hash table
281 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
282 ** entry points to.
283 **
284 ** Argument pContext is a pointer to the interpreter structure.
 
 
285 */
286 static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287 Th_Command *pCommand = (Th_Command *)pEntry->pData;
288 if( pCommand->xDel ){
289 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290 }
291 Th_Free((Th_Interp *)pContext, pEntry->pData);
292 pEntry->pData = 0;
 
293 }
294
295 /*
296 ** Push a new frame onto the stack.
297 */
@@ -311,19 +319,19 @@
311 Th_HashDelete(interp, pFrame->paVar);
312 interp->pFrame = pFrame->pCaller;
313 }
314
315 /*
316 ** The first part of the string (zInput,nInput) contains an escape
317 ** sequence. Set *pnEscape to the number of bytes in the escape sequence.
318 ** If there is a parse error, return TH_ERROR and set the interpreter
319 ** result to an error message. Otherwise return TH_OK.
320 */
321 static int thNextEscape(
322 Th_Interp *interp,
323 const char *zInput,
324 int nInput,
325 int *pnEscape
326 ){
327 int i = 2;
328
329 assert(nInput>0);
@@ -344,18 +352,18 @@
344 return TH_OK;
345 }
346
347 /*
348 ** The first part of the string (zInput,nInput) contains a variable
349 ** reference. Set *pnVarname to the number of bytes in the variable
350 ** reference. If there is a parse error, return TH_ERROR and set the
351 ** interpreter result to an error message. Otherwise return TH_OK.
352 */
353 int thNextVarname(
354 Th_Interp *interp,
355 const char *zInput,
356 int nInput,
357 int *pnVarname
358 ){
359 int i;
360
361 assert(nInput>0);
@@ -401,19 +409,19 @@
401 return TH_OK;
402 }
403
404 /*
405 ** The first part of the string (zInput,nInput) contains a command
406 ** enclosed in a "[]" block. Set *pnCommand to the number of bytes in
407 ** the variable reference. If there is a parse error, return TH_ERROR
408 ** and set the interpreter result to an error message. Otherwise return
409 ** TH_OK.
410 */
411 int thNextCommand(
412 Th_Interp *interp,
413 const char *zInput,
414 int nInput,
415 int *pnCommand
416 ){
417 int nBrace = 0;
418 int nSquare = 0;
419 int i;
@@ -438,17 +446,17 @@
438
439 return TH_OK;
440 }
441
442 /*
443 ** Set *pnSpace to the number of whitespace bytes at the start of
444 ** input string (zInput, nInput). Always return TH_OK.
445 */
446 int thNextSpace(
447 Th_Interp *interp,
448 const char *zInput,
449 int nInput,
450 int *pnSpace
451 ){
452 int i;
453 for(i=0; i<nInput && th_isspace(zInput[i]); i++);
454 *pnSpace = i;
@@ -457,21 +465,21 @@
457
458 /*
459 ** The first byte of the string (zInput,nInput) is not white-space.
460 ** Set *pnWord to the number of bytes in the th1 word that starts
461 ** with this byte. If a complete word cannot be parsed or some other
462 ** error occurs, return TH_ERROR and set the interpreter result to
463 ** an error message. Otherwise return TH_OK.
464 **
465 ** If the isCmd argument is non-zero, then an unescaped ";" byte not
466 ** located inside of a block or quoted string is considered to mark
467 ** the end of the word.
468 */
469 static int thNextWord(
470 Th_Interp *interp,
471 const char *zInput,
472 int nInput,
473 int *pnWord,
474 int isCmd
475 ){
476 int iEnd = 0;
477
@@ -531,12 +539,12 @@
531 return thEvalLocal(interp, &zWord[1], nWord-2);
532 }
533
534 /*
535 ** The input string (zWord, nWord) contains a th1 variable reference
536 ** (a '$' byte followed by a variable name). Perform substitution on
537 ** the input string and store the resulting string in the interpreter
538 ** result.
539 */
540 static int thSubstVarname(
541 Th_Interp *interp,
542 const char *zWord,
@@ -572,11 +580,11 @@
572 return Th_GetVar(interp, &zWord[1], nWord-1);
573 }
574
575 /*
576 ** The input string (zWord, nWord) contains a th1 escape sequence.
577 ** Perform substitution on the input string and store the resulting
578 ** string in the interpreter result.
579 */
580 static int thSubstEscape(
581 Th_Interp *interp,
582 const char *zWord,
@@ -608,11 +616,11 @@
608 return TH_OK;
609 }
610
611 /*
612 ** The input string (zWord, nWord) contains a th1 word. Perform
613 ** substitution on the input string and store the resulting
614 ** string in the interpreter result.
615 */
616 static int thSubstWord(
617 Th_Interp *interp,
618 const char *zWord,
@@ -640,20 +648,20 @@
640 int (*xGet)(Th_Interp *, const char*, int, int *) = 0;
641 int (*xSubst)(Th_Interp *, const char*, int) = 0;
642
643 switch( zWord[i] ){
644 case '\\':
645 xGet = thNextEscape; xSubst = thSubstEscape;
646 break;
647 case '[':
648 if( !interp->isListMode ){
649 xGet = thNextCommand; xSubst = thSubstCommand;
650 break;
651 }
652 case '$':
653 if( !interp->isListMode ){
654 xGet = thNextVarname; xSubst = thSubstVarname;
655 break;
656 }
657 default: {
658 thBufferWrite(interp, &output, &zWord[i], 1);
659 continue; /* Go to the next iteration of the for(...) loop */
@@ -685,11 +693,11 @@
685 ** Return true if one of the following is true of the buffer pointed
686 ** to by zInput, length nInput:
687 **
688 ** + It is empty, or
689 ** + It contains nothing but white-space, or
690 ** + It contains no non-white-space characters before the first
691 ** newline character.
692 **
693 ** Otherwise return false.
694 */
695 static int thEndOfLine(const char *zInput, int nInput){
@@ -725,16 +733,16 @@
725 ** // Free all memory allocated by Th_SplitList(). The arrays pointed
726 ** // to by argv and argl are invalidated by this call.
727 ** //
728 ** Th_Free(interp, argv);
729 **
730 */
731 static int thSplitList(
732 Th_Interp *interp, /* Interpreter context */
733 const char *zList, /* Pointer to buffer containing input list */
734 int nList, /* Size of buffer pointed to by zList */
735 char ***pazElem, /* OUT: Array of list elements */
736 int **panElem, /* OUT: Lengths of each list element */
737 int *pnCount /* OUT: Number of list elements */
738 ){
739 int rc = TH_OK;
740
@@ -774,14 +782,14 @@
774 assert((lenbuf.nBuf/sizeof(int))==nCount);
775
776 assert((pazElem && panElem) || (!pazElem && !panElem));
777 if( pazElem && rc==TH_OK ){
778 int i;
779 char *zElem;
780 int *anElem;
781 char **azElem = Th_Malloc(interp,
782 sizeof(char*) * nCount + /* azElem */
783 sizeof(int) * nCount + /* anElem */
784 strbuf.nBuf /* space for list element strings */
785 );
786 anElem = (int *)&azElem[nCount];
787 zElem = (char *)&anElem[nCount];
@@ -795,11 +803,11 @@
795 *panElem = anElem;
796 }
797 if( pnCount ){
798 *pnCount = nCount;
799 }
800
801 finish:
802 thBufferFree(interp, &strbuf);
803 thBufferFree(interp, &lenbuf);
804 return rc;
805 }
@@ -876,18 +884,18 @@
876 if( rc==TH_OK ){
877 Th_Command *p = (Th_Command *)(pEntry->pData);
878 const char **azArg = (const char **)argv;
879 rc = p->xProc(interp, p->pContext, argc, azArg, argl);
880 }
881
882 /* If an error occurred, add this command to the stack trace report. */
883 if( rc==TH_ERROR ){
884 char *zRes;
885 int nRes;
886 char *zStack = 0;
887 int nStack = 0;
888
889 zRes = Th_TakeResult(interp, &nRes);
890 if( TH_OK==Th_GetVar(interp, (char *)"::th_stack_trace", -1) ){
891 zStack = Th_TakeResult(interp, &nStack);
892 }
893 Th_ListAppend(interp, &zStack, &nStack, zFirst, zInput-zFirst);
@@ -912,15 +920,15 @@
912 **
913 ** Argument iFrame is interpreted as follows:
914 **
915 ** * If iFrame is 0, this means the current frame.
916 **
917 ** * If iFrame is negative, then the nth frame up the stack, where
918 ** n is the absolute value of iFrame. A value of -1 means the
919 ** calling procedure.
920 **
921 ** * If iFrame is +ve, then the nth frame from the bottom of the
922 ** stack. An iFrame value of 1 means the toplevel (global) frame.
923 */
924 static Th_Frame *getFrame(Th_Interp *interp, int iFrame){
925 Th_Frame *p = interp->pFrame;
926 int i;
@@ -948,28 +956,28 @@
948
949
950 /*
951 ** Evaluate th1 script (zProgram, nProgram) in the frame identified by
952 ** argument iFrame. Leave either an error message or a result in the
953 ** interpreter result and return a th1 error code (TH_OK, TH_ERROR,
954 ** TH_RETURN, TH_CONTINUE or TH_BREAK).
955 */
956 int Th_Eval(Th_Interp *interp, int iFrame, const char *zProgram, int nProgram){
957 int rc = TH_OK;
958 Th_Frame *pSavedFrame = interp->pFrame;
959
960 /* Set Th_Interp.pFrame to the frame that this script is to be
961 ** evaluated in. The current frame is saved in pSavedFrame and will
962 ** be restored before this function returns.
963 */
964 interp->pFrame = getFrame(interp, iFrame);
965
966 if( !interp->pFrame ){
967 rc = TH_ERROR;
968 }else{
969 int nInput = nProgram;
970
971 if( nInput<0 ){
972 nInput = th_strlen(zProgram);
973 }
974 rc = thEvalLocal(interp, zProgram, nInput);
975 }
@@ -995,13 +1003,13 @@
995 ** array key name.
996 */
997 static int thAnalyseVarname(
998 const char *zVarname,
999 int nVarname,
1000 const char **pzOuter, /* OUT: Pointer to scalar/array name */
1001 int *pnOuter, /* OUT: Number of bytes at *pzOuter */
1002 const char **pzInner, /* OUT: Pointer to array key (or null) */
1003 int *pnInner, /* OUT: Number of bytes at *pzInner */
1004 int *pisGlobal /* OUT: Set to true if this is a global ref */
1005 ){
1006 const char *zOuter = zVarname;
1007 int nOuter;
@@ -1042,13 +1050,28 @@
1042 *pnInner = nInner;
1043 *pisGlobal = isGlobal;
1044 return TH_OK;
1045 }
1046
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1047 /*
1048 ** Input string (zVar, nVar) contains a variable name. This function locates
1049 ** the Th_Variable structure associated with the named variable. The
1050 ** variable name may be a global or local scalar or array variable
1051 **
1052 ** If the create argument is non-zero and the named variable does not exist
1053 ** it is created. Otherwise, an error is left in the interpreter result
1054 ** and NULL returned.
@@ -1055,16 +1078,19 @@
1055 **
1056 ** If the arrayok argument is false and the named variable is an array,
1057 ** an error is left in the interpreter result and NULL returned. If
1058 ** arrayok is true an array name is Ok.
1059 */
 
1060 static Th_Variable *thFindValue(
1061 Th_Interp *interp,
1062 const char *zVar, /* Pointer to variable name */
1063 int nVar, /* Number of bytes at nVar */
1064 int create, /* If true, create the variable if not found */
1065 int arrayok /* If true, an array is Ok. Otherwise array==error */
 
 
1066 ){
1067 const char *zOuter;
1068 int nOuter;
1069 const char *zInner;
1070 int nInner;
@@ -1073,16 +1099,24 @@
1073 Th_HashEntry *pEntry;
1074 Th_Frame *pFrame = interp->pFrame;
1075 Th_Variable *pValue;
1076
1077 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
 
 
 
 
 
1078 if( isGlobal ){
1079 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1080 }
1081
1082 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1083 assert(pEntry || !create);
 
 
 
1084 if( !pEntry ){
1085 goto no_such_var;
1086 }
1087
1088 pValue = (Th_Variable *)pEntry->pData;
@@ -1093,20 +1127,26 @@
1093 pEntry->pData = (void *)pValue;
1094 }
1095
1096 if( zInner ){
1097 if( pValue->zData ){
1098 Th_ErrorMessage(interp, "variable is a scalar:", zOuter, nOuter);
 
 
1099 return 0;
1100 }
1101 if( !pValue->pHash ){
1102 if( !create ){
1103 goto no_such_var;
1104 }
1105 pValue->pHash = Th_HashNew(interp);
1106 }
1107 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
 
 
 
 
1108 if( !pEntry ){
1109 goto no_such_var;
1110 }
1111 pValue = (Th_Variable *)pEntry->pData;
1112 if( !pValue ){
@@ -1115,34 +1155,38 @@
1115 pValue->nRef = 1;
1116 pEntry->pData = (void *)pValue;
1117 }
1118 }else{
1119 if( pValue->pHash && !arrayok ){
1120 Th_ErrorMessage(interp, "variable is an array:", zOuter, nOuter);
 
 
1121 return 0;
1122 }
1123 }
1124
1125 return pValue;
1126
1127 no_such_var:
1128 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
 
 
1129 return 0;
1130 }
1131
1132 /*
1133 ** String (zVar, nVar) must contain the name of a scalar variable or
1134 ** array member. Look up the variable, store its current value in
1135 ** the interpreter result and return TH_OK.
1136 **
1137 ** If the named variable does not exist, return TH_ERROR and leave
1138 ** an error message in the interpreter result.
1139 */
1140 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1141 Th_Variable *pValue;
1142
1143 pValue = thFindValue(interp, zVar, nVar, 0, 0);
1144 if( !pValue ){
1145 return TH_ERROR;
1146 }
1147 if( !pValue->zData ){
1148 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1154,11 +1198,12 @@
1154
1155 /*
1156 ** Return true if variable (zVar, nVar) exists.
1157 */
1158 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1159 return thFindValue(interp, zVar, nVar, 0, 0)!=0;
 
1160 }
1161
1162 /*
1163 ** String (zVar, nVar) must contain the name of a scalar variable or
1164 ** array member. If the variable does not exist it is created. The
@@ -1166,19 +1211,19 @@
1166 **
1167 ** If (zVar, nVar) refers to an existing array, TH_ERROR is returned
1168 ** and an error message left in the interpreter result.
1169 */
1170 int Th_SetVar(
1171 Th_Interp *interp,
1172 const char *zVar,
1173 int nVar,
1174 const char *zValue,
1175 int nValue
1176 ){
1177 Th_Variable *pValue;
1178
1179 pValue = thFindValue(interp, zVar, nVar, 1, 0);
1180 if( !pValue ){
1181 return TH_ERROR;
1182 }
1183
1184 if( nValue<0 ){
@@ -1202,13 +1247,13 @@
1202 ** Create a variable link so that accessing variable (zLocal, nLocal) is
1203 ** the same as accessing variable (zLink, nLink) in stack frame iFrame.
1204 */
1205 int Th_LinkVar(
1206 Th_Interp *interp, /* Interpreter */
1207 const char *zLocal, int nLocal, /* Local varname */
1208 int iFrame, /* Stack frame of linked var */
1209 const char *zLink, int nLink /* Linked varname */
1210 ){
1211 Th_Frame *pSavedFrame = interp->pFrame;
1212 Th_Frame *pFrame;
1213 Th_HashEntry *pEntry;
1214 Th_Variable *pValue;
@@ -1217,11 +1262,11 @@
1217 if( !pFrame ){
1218 return TH_ERROR;
1219 }
1220 pSavedFrame = interp->pFrame;
1221 interp->pFrame = pFrame;
1222 pValue = thFindValue(interp, zLink, nLink, 1, 1);
1223 interp->pFrame = pSavedFrame;
1224
1225 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1226 if( pEntry->pData ){
1227 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1238,25 +1283,68 @@
1238 ** an array, or an array member. If the identified variable exists, it
1239 ** is deleted and TH_OK returned. Otherwise, an error message is left
1240 ** in the interpreter result and TH_ERROR is returned.
1241 */
1242 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
 
1243 Th_Variable *pValue;
 
 
1244
1245 pValue = thFindValue(interp, zVar, nVar, 1, 1);
1246 if( !pValue ){
1247 return TH_ERROR;
1248 }
1249
1250 Th_Free(interp, pValue->zData);
1251 pValue->zData = 0;
1252 if( pValue->pHash ){
1253 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1254 Th_HashDelete(interp, pValue->pHash);
1255 pValue->pHash = 0;
1256 }
1257 return TH_OK;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1258 }
1259
1260 /*
1261 ** Return an allocated buffer containing a copy of string (z, n). The
1262 ** caller is responsible for eventually calling Th_Free() to free
@@ -1291,11 +1379,11 @@
1291 if( interp ){
1292 char *zRes = 0;
1293 int nRes = 0;
1294
1295 Th_SetVar(interp, (char *)"::th_stack_trace", -1, 0, 0);
1296
1297 Th_StringAppend(interp, &zRes, &nRes, zPre, -1);
1298 if( zRes[nRes-1]=='"' ){
1299 Th_StringAppend(interp, &zRes, &nRes, z, n);
1300 Th_StringAppend(interp, &zRes, &nRes, (const char *)"\"", 1);
1301 }else{
@@ -1373,12 +1461,12 @@
1373 return (char *)Th_Malloc(pInterp, 1);
1374 }
1375 }
1376
1377
1378 /*
1379 ** Wrappers around the supplied malloc() and free()
1380 */
1381 void *Th_Malloc(Th_Interp *pInterp, int nByte){
1382 void *p = pInterp->pVtab->xMalloc(nByte);
1383 if( p ){
1384 memset(p, 0, nByte);
@@ -1390,16 +1478,16 @@
1390 pInterp->pVtab->xFree(z);
1391 }
1392 }
1393
1394 /*
1395 ** Install a new th1 command.
1396 **
1397 ** If a command of the same name already exists, it is deleted automatically.
1398 */
1399 int Th_CreateCommand(
1400 Th_Interp *interp,
1401 const char *zName, /* New command name */
1402 Th_CommandProc xProc, /* Command callback proc */
1403 void *pContext, /* Value to pass as second arg to xProc */
1404 void (*xDel)(Th_Interp *, void *) /* Command destructor callback */
1405 ){
@@ -1417,27 +1505,27 @@
1417 }
1418 pCommand->xProc = xProc;
1419 pCommand->pContext = pContext;
1420 pCommand->xDel = xDel;
1421 pEntry->pData = (void *)pCommand;
1422
1423 return TH_OK;
1424 }
1425
1426 /*
1427 ** Rename the existing command (zName, nName) to (zNew, nNew). If nNew is 0,
1428 ** the command is deleted instead of renamed.
1429 **
1430 ** If successful, TH_OK is returned. If command zName does not exist, or
1431 ** if command zNew already exists, an error message is left in the
1432 ** interpreter result and TH_ERROR is returned.
1433 */
1434 int Th_RenameCommand(
1435 Th_Interp *interp,
1436 const char *zName, /* Existing command name */
1437 int nName, /* Number of bytes at zName */
1438 const char *zNew, /* New command name */
1439 int nNew /* Number of bytes at zNew */
1440 ){
1441 Th_HashEntry *pEntry;
1442 Th_HashEntry *pNewEntry;
1443
@@ -1491,11 +1579,11 @@
1491 ** If an error occurs (if (zList, nList) is not a valid list) an error
1492 ** message is left in the interpreter result and TH_ERROR returned.
1493 **
1494 ** If successful, *pnCount is set to the number of elements in the list.
1495 ** panElem is set to point at an array of *pnCount integers - the lengths
1496 ** of the element values. *pazElem is set to point at an array of
1497 ** pointers to buffers containing the array element's data.
1498 **
1499 ** To free the arrays allocated at *pazElem and *panElem, the caller
1500 ** should call Th_Free() on *pazElem only. Exactly one such call to
1501 ** Th_Free() must be made per call to Th_SplitList().
@@ -1517,13 +1605,13 @@
1517 ** Th_Free(interp, azElem);
1518 **
1519 */
1520 int Th_SplitList(
1521 Th_Interp *interp,
1522 const char *zList, /* Pointer to buffer containing list */
1523 int nList, /* Number of bytes at zList */
1524 char ***pazElem, /* OUT: Array of pointers to element data */
1525 int **panElem, /* OUT: Array of element data lengths */
1526 int *pnCount /* OUT: Number of elements in list */
1527 ){
1528 int rc;
1529 interp->isListMode = 1;
@@ -1534,16 +1622,16 @@
1534 }
1535 return rc;
1536 }
1537
1538 /*
1539 ** Append a new element to an existing th1 list. The element to append
1540 ** to the list is (zElem, nElem).
1541 **
1542 ** A pointer to the existing list must be stored at *pzList when this
1543 ** function is called. The length must be stored in *pnList. The value
1544 ** of *pzList must either be NULL (in which case *pnList must be 0), or
1545 ** a pointer to memory obtained from Th_Malloc().
1546 **
1547 ** This function calls Th_Free() to free the buffer at *pzList and sets
1548 ** *pzList to point to a new buffer containing the new list value. *pnList
1549 ** is similarly updated before returning. The return value is always TH_OK.
@@ -1560,13 +1648,13 @@
1560 ** Th_Free(interp, zList);
1561 **
1562 */
1563 int Th_ListAppend(
1564 Th_Interp *interp, /* Interpreter context */
1565 char **pzList, /* IN/OUT: Ptr to ptr to list */
1566 int *pnList, /* IN/OUT: Current length of *pzList */
1567 const char *zElem, /* Data to append */
1568 int nElem /* Length of nElem */
1569 ){
1570 Buffer output;
1571 int i;
1572
@@ -1615,13 +1703,13 @@
1615 ** Append a new element to an existing th1 string. This function uses
1616 ** the same interface as the Th_ListAppend() function.
1617 */
1618 int Th_StringAppend(
1619 Th_Interp *interp, /* Interpreter context */
1620 char **pzStr, /* IN/OUT: Ptr to ptr to list */
1621 int *pnStr, /* IN/OUT: Current length of *pzStr */
1622 const char *zElem, /* Data to append */
1623 int nElem /* Length of nElem */
1624 ){
1625 char *zNew;
1626 int nNew;
1627
@@ -1639,11 +1727,11 @@
1639 *pnStr = nNew;
1640
1641 return TH_OK;
1642 }
1643
1644 /*
1645 ** Delete an interpreter.
1646 */
1647 void Th_DeleteInterp(Th_Interp *interp){
1648 assert(interp->pFrame);
1649 assert(0==interp->pFrame->pCaller);
@@ -1660,11 +1748,11 @@
1660
1661 /* Delete the interpreter structure itself. */
1662 Th_Free(interp, (void *)interp);
1663 }
1664
1665 /*
1666 ** Create a new interpreter.
1667 */
1668 Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){
1669 Th_Interp *p;
1670
@@ -1694,11 +1782,11 @@
1694 Operator *pOp;
1695 Expr *pParent;
1696 Expr *pLeft;
1697 Expr *pRight;
1698
1699 char *zValue; /* Pointer to literal value */
1700 int nValue; /* Length of literal value buffer */
1701 };
1702
1703 /* Unary operators */
1704 #define OP_UNARY_MINUS 2
@@ -1750,11 +1838,11 @@
1750 {"+", OP_UNARY_PLUS, 1, ARG_NUMBER},
1751 {"~", OP_BITWISE_NOT, 1, ARG_INTEGER},
1752 {"!", OP_LOGICAL_NOT, 1, ARG_INTEGER},
1753
1754 /* Binary operators. It is important to the parsing in Th_Expr() that
1755 * the two-character symbols ("==") appear before the one-character
1756 * ones ("="). And that the priorities of all binary operators are
1757 * integers between 2 and 12.
1758 */
1759 {"<<", OP_LEFTSHIFT, 4, ARG_INTEGER},
1760 {">>", OP_RIGHTSHIFT, 4, ARG_INTEGER},
@@ -1781,16 +1869,16 @@
1781 {0,0,0,0}
1782 };
1783
1784 /*
1785 ** The first part of the string (zInput,nInput) contains a number.
1786 ** Set *pnVarname to the number of bytes in the numeric string.
1787 */
1788 static int thNextNumber(
1789 Th_Interp *interp,
1790 const char *zInput,
1791 int nInput,
1792 int *pnLiteral
1793 ){
1794 int i;
1795 int seenDot = 0;
1796 for(i=0; i<nInput; i++){
@@ -1856,11 +1944,11 @@
1856 if( eArgType==ARG_NUMBER ){
1857 if( (zLeft==0 || TH_OK==Th_ToInt(0, zLeft, nLeft, &iLeft))
1858 && (zRight==0 || TH_OK==Th_ToInt(0, zRight, nRight, &iRight))
1859 ){
1860 eArgType = ARG_INTEGER;
1861 }else if(
1862 (zLeft && TH_OK!=Th_ToDouble(interp, zLeft, nLeft, &fLeft)) ||
1863 (zRight && TH_OK!=Th_ToDouble(interp, zRight, nRight, &fRight))
1864 ){
1865 /* A type error. */
1866 rc = TH_ERROR;
@@ -1868,28 +1956,30 @@
1868 }else if( eArgType==ARG_INTEGER ){
1869 rc = Th_ToInt(interp, zLeft, nLeft, &iLeft);
1870 if( rc==TH_OK && zRight ){
1871 rc = Th_ToInt(interp, zRight, nRight, &iRight);
1872 }
1873 }
1874 }
1875
1876 if( rc==TH_OK && eArgType==ARG_INTEGER ){
1877 int iRes = 0;
1878 switch( pExpr->pOp->eOp ) {
1879 case OP_MULTIPLY: iRes = iLeft*iRight; break;
1880 case OP_DIVIDE:
1881 if(!iRight){
1882 Th_ErrorMessage(interp, "Divide by 0:", zLeft, nLeft);
1883 return TH_ERROR;
 
1884 }
1885 iRes = iLeft/iRight;
1886 break;
1887 case OP_MODULUS:
1888 if(!iRight){
1889 Th_ErrorMessage(interp, "Modulo by 0:", zLeft, nLeft);
1890 return TH_ERROR;
 
1891 }
1892 iRes = iLeft%iRight;
1893 break;
1894 case OP_ADD: iRes = iLeft+iRight; break;
1895 case OP_SUBTRACT: iRes = iLeft-iRight; break;
@@ -1913,11 +2003,18 @@
1913 }
1914 Th_SetResultInt(interp, iRes);
1915 }else if( rc==TH_OK && eArgType==ARG_NUMBER ){
1916 switch( pExpr->pOp->eOp ) {
1917 case OP_MULTIPLY: Th_SetResultDouble(interp, fLeft*fRight); break;
1918 case OP_DIVIDE: Th_SetResultDouble(interp, fLeft/fRight); break;
 
 
 
 
 
 
 
1919 case OP_ADD: Th_SetResultDouble(interp, fLeft+fRight); break;
1920 case OP_SUBTRACT: Th_SetResultDouble(interp, fLeft-fRight); break;
1921 case OP_LT: Th_SetResultInt(interp, fLeft<fRight); break;
1922 case OP_GT: Th_SetResultInt(interp, fLeft>fRight); break;
1923 case OP_LE: Th_SetResultInt(interp, fLeft<=fRight); break;
@@ -1936,10 +2033,12 @@
1936 case OP_SEQ: Th_SetResultInt(interp, iEqual); break;
1937 case OP_SNE: Th_SetResultInt(interp, !iEqual); break;
1938 default: assert(!"Internal error");
1939 }
1940 }
 
 
1941
1942 Th_Free(interp, zLeft);
1943 Th_Free(interp, zRight);
1944 }
1945
@@ -1959,11 +2058,11 @@
1959 #define ISTERM(x) (apToken[x] && (!apToken[x]->pOp || apToken[x]->pLeft))
1960
1961 for(jj=0; jj<nToken; jj++){
1962 if( apToken[jj]->pOp && apToken[jj]->pOp->eOp==OP_OPEN_BRACKET ){
1963 int nNest = 1;
1964 int iLeft = jj;
1965
1966 for(jj++; jj<nToken; jj++){
1967 Operator *pOp = apToken[jj]->pOp;
1968 if( pOp && pOp->eOp==OP_OPEN_BRACKET ) nNest++;
1969 if( pOp && pOp->eOp==OP_CLOSE_BRACKET ) nNest--;
@@ -2033,11 +2132,11 @@
2033 /*
2034 ** Parse a string containing a TH expression to a list of tokens.
2035 */
2036 static int exprParse(
2037 Th_Interp *interp, /* Interpreter to leave error message in */
2038 const char *zExpr, /* Pointer to input string */
2039 int nExpr, /* Number of bytes at zExpr */
2040 Expr ***papToken, /* OUT: Array of tokens. */
2041 int *pnToken /* OUT: Size of token array */
2042 ){
2043 int i;
@@ -2108,11 +2207,11 @@
2108 memcpy(pNew->zValue, z, pNew->nValue);
2109 i += pNew->nValue;
2110 }
2111 if( (nToken%16)==0 ){
2112 /* Grow the apToken array. */
2113 Expr **apTokenOld = apToken;
2114 apToken = Th_Malloc(interp, sizeof(Expr *)*(nToken+16));
2115 memcpy(apToken, apTokenOld, sizeof(Expr *)*nToken);
2116 }
2117
2118 /* Put the new token at the end of the apToken array */
@@ -2133,11 +2232,11 @@
2133 /*
2134 ** Evaluate the string (zExpr, nExpr) as a Th expression. Store
2135 ** the result in the interpreter interp and return TH_OK if
2136 ** successful. If an error occurs, store an error message in
2137 ** the interpreter result and return an error code.
2138 */
2139 int Th_Expr(Th_Interp *interp, const char *zExpr, int nExpr){
2140 int rc; /* Return Code */
2141 int i; /* Loop counter */
2142
2143 int nToken = 0;
@@ -2150,11 +2249,11 @@
2150 /* Parse the expression to a list of tokens. */
2151 rc = exprParse(interp, zExpr, nExpr, &apToken, &nToken);
2152
2153 /* If the parsing was successful, create an expression tree from
2154 ** the parsed list of tokens. If successful, apToken[0] is set
2155 ** to point to the root of the expression tree.
2156 */
2157 if( rc==TH_OK ){
2158 rc = exprMakeTree(interp, apToken, nToken);
2159 }
2160
@@ -2188,16 +2287,17 @@
2188
2189 /*
2190 ** Iterate through all values currently stored in the hash table. Invoke
2191 ** the callback function xCallback for each entry. The second argument
2192 ** passed to xCallback is a copy of the fourth argument passed to this
2193 ** function.
 
2194 */
2195 void Th_HashIterate(
2196 Th_Interp *interp,
2197 Th_Hash *pHash,
2198 void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2199 void *pContext
2200 ){
2201 int i;
2202 for(i=0; i<TH_HASHSIZE; i++){
2203 Th_HashEntry *pEntry;
@@ -2208,14 +2308,15 @@
2208 }
2209 }
2210 }
2211
2212 /*
2213 ** Helper function for Th_HashDelete().
2214 */
2215 static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2216 Th_Free((Th_Interp *)pContext, (void *)pEntry);
 
2217 }
2218
2219 /*
2220 ** Free a hash-table previously allocated by Th_HashNew().
2221 */
@@ -2225,14 +2326,14 @@
2225 Th_Free(interp, pHash);
2226 }
2227 }
2228
2229 /*
2230 ** This function is used to insert or delete hash table items, or to
2231 ** query a hash table for an existing item.
2232 **
2233 ** If parameter op is less than zero, then the hash-table element
2234 ** identified by (zKey, nKey) is removed from the hash-table if it
2235 ** exists. NULL is returned.
2236 **
2237 ** Otherwise, if the hash-table contains an item with key (zKey, nKey),
2238 ** a pointer to the associated Th_HashEntry is returned. If parameter
@@ -2239,11 +2340,11 @@
2239 ** op is greater than zero, then a new entry is added if one cannot
2240 ** be found. If op is zero, then NULL is returned if the item is
2241 ** not already present in the hash-table.
2242 */
2243 Th_HashEntry *Th_HashFind(
2244 Th_Interp *interp,
2245 Th_Hash *pHash,
2246 const char *zKey,
2247 int nKey,
2248 int op /* -ve = delete, 0 = find, +ve = insert */
2249 ){
@@ -2307,11 +2408,11 @@
2307 ** '\f' 0x0C
2308 ** '\r' 0x0D
2309 **
2310 ** Whitespace characters have the 0x01 flag set. Decimal digits have the
2311 ** 0x2 flag set. Single byte printable characters have the 0x4 flag set.
2312 ** Alphabet characters have the 0x8 bit set.
2313 **
2314 ** The special list characters have the 0x10 flag set
2315 **
2316 ** { } [ ] \ ; ' "
2317 **
@@ -2458,14 +2559,14 @@
2458 return z - zBegin;
2459 }
2460
2461 /*
2462 ** Try to convert the string passed as arguments (z, n) to an integer.
2463 ** If successful, store the result in *piOut and return TH_OK.
2464 **
2465 ** If the string cannot be converted to an integer, return TH_ERROR.
2466 ** If the interp argument is not NULL, leave an error message in the
2467 ** interpreter result too.
2468 */
2469 int Th_ToInt(Th_Interp *interp, const char *z, int n, int *piOut){
2470 int i = 0;
2471 int iOut = 0;
@@ -2493,20 +2594,20 @@
2493 return TH_OK;
2494 }
2495
2496 /*
2497 ** Try to convert the string passed as arguments (z, n) to a double.
2498 ** If successful, store the result in *pfOut and return TH_OK.
2499 **
2500 ** If the string cannot be converted to a double, return TH_ERROR.
2501 ** If the interp argument is not NULL, leave an error message in the
2502 ** interpreter result too.
2503 */
2504 int Th_ToDouble(
2505 Th_Interp *interp,
2506 const char *z,
2507 int n,
2508 double *pfOut
2509 ){
2510 if( !sqlite3IsNumber((const char *)z, 0) ){
2511 Th_ErrorMessage(interp, "expected number, got: \"", z, n);
2512 return TH_ERROR;
@@ -2547,33 +2648,33 @@
2547 ** the double fVal and return TH_OK.
2548 */
2549 int Th_SetResultDouble(Th_Interp *interp, double fVal){
2550 int i; /* Iterator variable */
2551 double v = fVal; /* Input value */
2552 char zBuf[128]; /* Output buffer */
2553 char *z = zBuf; /* Output cursor */
2554 int iDot = 0; /* Digit after which to place decimal point */
2555 int iExp = 0; /* Exponent (NN in eNN) */
2556 const char *zExp; /* String representation of iExp */
2557
2558 /* Precision: */
2559 #define INSIGNIFICANT 0.000000000001
2560 #define ROUNDER 0.0000000000005
2561 double insignificant = INSIGNIFICANT;
2562
2563 /* If the real value is negative, write a '-' character to the
2564 * output and transform v to the corresponding positive number.
2565 */
2566 if( v<0.0 ){
2567 *z++ = '-';
2568 v *= -1.0;
2569 }
2570
2571 /* Normalize v to a value between 1.0 and 10.0. Integer
2572 * variable iExp is set to the exponent. i.e the original
2573 * value is (v * 10^iExp) (or the negative thereof).
2574 */
2575 if( v>0.0 ){
2576 while( (v+ROUNDER)>=10.0 ) { iExp++; v *= 0.1; }
2577 while( (v+ROUNDER)<1.0 ) { iExp--; v *= 10.0; }
2578 }
2579 v += ROUNDER;
2580
--- src/th.c
+++ src/th.c
@@ -1,8 +1,8 @@
1
2 /*
3 ** The implementation of the TH core. This file contains the parser, and
4 ** the implementation of the interface in th.h.
5 */
6
7 #include "config.h"
8 #include "th.h"
@@ -16,11 +16,11 @@
16 /*
17 ** Interpreter structure.
18 */
19 struct Th_Interp {
20 Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */
21 char *zResult; /* Current interpreter result (Th_Malloc()ed) */
22 int nResult; /* number of bytes in zResult */
23 Th_Hash *paCmd; /* Table of registered commands */
24 Th_Frame *pFrame; /* Current execution frame */
25 int isListMode; /* True if thSplitList() should operate in "list" mode */
26 };
@@ -42,25 +42,25 @@
42 ** are stored in the Th_Frame.paVar hash table member of the associated
43 ** stack frame object.
44 **
45 ** When an interpreter is created, a single Th_Frame structure is also
46 ** allocated - the global variable scope. Th_Interp.pFrame (the current
47 ** interpreter frame) is initialised to point to this Th_Frame. It is
48 ** not deleted for the lifetime of the interpreter (because the global
49 ** frame never goes out of scope).
50 **
51 ** New stack frames are created by the Th_InFrame() function. Before
52 ** invoking its callback function, Th_InFrame() allocates a new Th_Frame
53 ** structure with pCaller set to the current frame (Th_Interp.pFrame),
54 ** and sets the current frame to the new frame object. After the callback
55 ** has been invoked, the allocated Th_Frame is deleted and the value
56 ** of the current frame pointer restored.
57 **
58 ** By default, the Th_SetVar(), Th_UnsetVar() and Th_GetVar() functions
59 ** access variable values in the current frame. If they need to access
60 ** the global frame, they do so by traversing the pCaller pointer list.
61 ** Likewise, the Th_LinkVar() function uses the pCaller pointers to
62 ** link to variables located in the global or other stack frames.
63 */
64 struct Th_Frame {
65 Th_Hash *paVar; /* Variables defined in this scope */
66 Th_Frame *pCaller; /* Calling frame */
@@ -84,11 +84,11 @@
84 ** value.
85 */
86 struct Th_Variable {
87 int nRef; /* Number of references to this structure */
88 int nData; /* Number of bytes at Th_Variable.zData */
89 char *zData; /* Data for scalar variables */
90 Th_Hash *pHash; /* Data for array variables */
91 };
92
93 /*
94 ** Hash table API:
@@ -105,24 +105,24 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static int thFreeVariable(Th_HashEntry*, void*);
111 static int thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
117 ** sequence etc.), these functions determine the number of bytes
118 ** of the input consumed by the construct. For example:
119 **
120 ** int nByte;
121 ** thNextCommand(interp, "[expr $a+1] $nIter", 18, &nByte);
122 **
123 ** results in variable nByte being set to 11. Or,
124 **
125 ** thNextVarname(interp, "$a+1", 4, &nByte);
126 **
127 ** results in nByte being set to 2.
128 */
@@ -132,24 +132,24 @@
132 static int thNextNumber (Th_Interp*, const char *z, int n, int *pN);
133 static int thNextSpace (Th_Interp*, const char *z, int n, int *pN);
134
135 /*
136 ** Given that the input string (z, n) contains a language construct of
137 ** the relevant type (a command enclosed in [], an escape sequence
138 ** like "\xFF" or a variable reference like "${varname}", perform
139 ** substitution on the string and store the resulting string in
140 ** the interpreter result.
141 */
142 static int thSubstCommand(Th_Interp*, const char *z, int n);
143 static int thSubstEscape (Th_Interp*, const char *z, int n);
144 static int thSubstVarname(Th_Interp*, const char *z, int n);
145
146 /*
147 ** Given that there is a th1 word located at the start of the input
148 ** string (z, n), determine the length in bytes of that word. If the
149 ** isCmd argument is non-zero, then an unescaped ";" byte not
150 ** located inside of a block or quoted string is considered to mark
151 ** the end of the word.
152 */
153 static int thNextWord(Th_Interp*, const char *z, int n, int *pN, int isCmd);
154
155 /*
@@ -176,13 +176,13 @@
176 ** Append nAdd bytes of content copied from zAdd to the end of buffer
177 ** pBuffer. If there is not enough space currently allocated, resize
178 ** the allocation to make space.
179 */
180 static int thBufferWrite(
181 Th_Interp *interp,
182 Buffer *pBuffer,
183 const char *zAdd,
184 int nAdd
185 ){
186 int nReq;
187
188 if( nAdd<0 ){
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
263 **
264 ** Returns non-zero if the Th_Variable was actually freed.
265 */
266 static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
267 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
268 pValue->nRef--;
269 assert( pValue->nRef>=0 );
270 if( pValue->nRef==0 ){
271 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
273 if( pValue->pHash ){
274 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
275 Th_HashDelete(interp, pValue->pHash);
276 }
277 Th_Free(interp, pValue);
278 pEntry->pData = 0;
279 return 1;
280 }
281 return 0;
282 }
283
284 /*
285 ** Argument pEntry points to an entry in the command hash table
286 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
287 ** entry points to.
288 **
289 ** Argument pContext is a pointer to the interpreter structure.
290 **
291 ** Always returns non-zero.
292 */
293 static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
294 Th_Command *pCommand = (Th_Command *)pEntry->pData;
295 if( pCommand->xDel ){
296 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
297 }
298 Th_Free((Th_Interp *)pContext, pEntry->pData);
299 pEntry->pData = 0;
300 return 1;
301 }
302
303 /*
304 ** Push a new frame onto the stack.
305 */
@@ -311,19 +319,19 @@
319 Th_HashDelete(interp, pFrame->paVar);
320 interp->pFrame = pFrame->pCaller;
321 }
322
323 /*
324 ** The first part of the string (zInput,nInput) contains an escape
325 ** sequence. Set *pnEscape to the number of bytes in the escape sequence.
326 ** If there is a parse error, return TH_ERROR and set the interpreter
327 ** result to an error message. Otherwise return TH_OK.
328 */
329 static int thNextEscape(
330 Th_Interp *interp,
331 const char *zInput,
332 int nInput,
333 int *pnEscape
334 ){
335 int i = 2;
336
337 assert(nInput>0);
@@ -344,18 +352,18 @@
352 return TH_OK;
353 }
354
355 /*
356 ** The first part of the string (zInput,nInput) contains a variable
357 ** reference. Set *pnVarname to the number of bytes in the variable
358 ** reference. If there is a parse error, return TH_ERROR and set the
359 ** interpreter result to an error message. Otherwise return TH_OK.
360 */
361 int thNextVarname(
362 Th_Interp *interp,
363 const char *zInput,
364 int nInput,
365 int *pnVarname
366 ){
367 int i;
368
369 assert(nInput>0);
@@ -401,19 +409,19 @@
409 return TH_OK;
410 }
411
412 /*
413 ** The first part of the string (zInput,nInput) contains a command
414 ** enclosed in a "[]" block. Set *pnCommand to the number of bytes in
415 ** the variable reference. If there is a parse error, return TH_ERROR
416 ** and set the interpreter result to an error message. Otherwise return
417 ** TH_OK.
418 */
419 int thNextCommand(
420 Th_Interp *interp,
421 const char *zInput,
422 int nInput,
423 int *pnCommand
424 ){
425 int nBrace = 0;
426 int nSquare = 0;
427 int i;
@@ -438,17 +446,17 @@
446
447 return TH_OK;
448 }
449
450 /*
451 ** Set *pnSpace to the number of whitespace bytes at the start of
452 ** input string (zInput, nInput). Always return TH_OK.
453 */
454 int thNextSpace(
455 Th_Interp *interp,
456 const char *zInput,
457 int nInput,
458 int *pnSpace
459 ){
460 int i;
461 for(i=0; i<nInput && th_isspace(zInput[i]); i++);
462 *pnSpace = i;
@@ -457,21 +465,21 @@
465
466 /*
467 ** The first byte of the string (zInput,nInput) is not white-space.
468 ** Set *pnWord to the number of bytes in the th1 word that starts
469 ** with this byte. If a complete word cannot be parsed or some other
470 ** error occurs, return TH_ERROR and set the interpreter result to
471 ** an error message. Otherwise return TH_OK.
472 **
473 ** If the isCmd argument is non-zero, then an unescaped ";" byte not
474 ** located inside of a block or quoted string is considered to mark
475 ** the end of the word.
476 */
477 static int thNextWord(
478 Th_Interp *interp,
479 const char *zInput,
480 int nInput,
481 int *pnWord,
482 int isCmd
483 ){
484 int iEnd = 0;
485
@@ -531,12 +539,12 @@
539 return thEvalLocal(interp, &zWord[1], nWord-2);
540 }
541
542 /*
543 ** The input string (zWord, nWord) contains a th1 variable reference
544 ** (a '$' byte followed by a variable name). Perform substitution on
545 ** the input string and store the resulting string in the interpreter
546 ** result.
547 */
548 static int thSubstVarname(
549 Th_Interp *interp,
550 const char *zWord,
@@ -572,11 +580,11 @@
580 return Th_GetVar(interp, &zWord[1], nWord-1);
581 }
582
583 /*
584 ** The input string (zWord, nWord) contains a th1 escape sequence.
585 ** Perform substitution on the input string and store the resulting
586 ** string in the interpreter result.
587 */
588 static int thSubstEscape(
589 Th_Interp *interp,
590 const char *zWord,
@@ -608,11 +616,11 @@
616 return TH_OK;
617 }
618
619 /*
620 ** The input string (zWord, nWord) contains a th1 word. Perform
621 ** substitution on the input string and store the resulting
622 ** string in the interpreter result.
623 */
624 static int thSubstWord(
625 Th_Interp *interp,
626 const char *zWord,
@@ -640,20 +648,20 @@
648 int (*xGet)(Th_Interp *, const char*, int, int *) = 0;
649 int (*xSubst)(Th_Interp *, const char*, int) = 0;
650
651 switch( zWord[i] ){
652 case '\\':
653 xGet = thNextEscape; xSubst = thSubstEscape;
654 break;
655 case '[':
656 if( !interp->isListMode ){
657 xGet = thNextCommand; xSubst = thSubstCommand;
658 break;
659 }
660 case '$':
661 if( !interp->isListMode ){
662 xGet = thNextVarname; xSubst = thSubstVarname;
663 break;
664 }
665 default: {
666 thBufferWrite(interp, &output, &zWord[i], 1);
667 continue; /* Go to the next iteration of the for(...) loop */
@@ -685,11 +693,11 @@
693 ** Return true if one of the following is true of the buffer pointed
694 ** to by zInput, length nInput:
695 **
696 ** + It is empty, or
697 ** + It contains nothing but white-space, or
698 ** + It contains no non-white-space characters before the first
699 ** newline character.
700 **
701 ** Otherwise return false.
702 */
703 static int thEndOfLine(const char *zInput, int nInput){
@@ -725,16 +733,16 @@
733 ** // Free all memory allocated by Th_SplitList(). The arrays pointed
734 ** // to by argv and argl are invalidated by this call.
735 ** //
736 ** Th_Free(interp, argv);
737 **
738 */
739 static int thSplitList(
740 Th_Interp *interp, /* Interpreter context */
741 const char *zList, /* Pointer to buffer containing input list */
742 int nList, /* Size of buffer pointed to by zList */
743 char ***pazElem, /* OUT: Array of list elements */
744 int **panElem, /* OUT: Lengths of each list element */
745 int *pnCount /* OUT: Number of list elements */
746 ){
747 int rc = TH_OK;
748
@@ -774,14 +782,14 @@
782 assert((lenbuf.nBuf/sizeof(int))==nCount);
783
784 assert((pazElem && panElem) || (!pazElem && !panElem));
785 if( pazElem && rc==TH_OK ){
786 int i;
787 char *zElem;
788 int *anElem;
789 char **azElem = Th_Malloc(interp,
790 sizeof(char*) * nCount + /* azElem */
791 sizeof(int) * nCount + /* anElem */
792 strbuf.nBuf /* space for list element strings */
793 );
794 anElem = (int *)&azElem[nCount];
795 zElem = (char *)&anElem[nCount];
@@ -795,11 +803,11 @@
803 *panElem = anElem;
804 }
805 if( pnCount ){
806 *pnCount = nCount;
807 }
808
809 finish:
810 thBufferFree(interp, &strbuf);
811 thBufferFree(interp, &lenbuf);
812 return rc;
813 }
@@ -876,18 +884,18 @@
884 if( rc==TH_OK ){
885 Th_Command *p = (Th_Command *)(pEntry->pData);
886 const char **azArg = (const char **)argv;
887 rc = p->xProc(interp, p->pContext, argc, azArg, argl);
888 }
889
890 /* If an error occurred, add this command to the stack trace report. */
891 if( rc==TH_ERROR ){
892 char *zRes;
893 int nRes;
894 char *zStack = 0;
895 int nStack = 0;
896
897 zRes = Th_TakeResult(interp, &nRes);
898 if( TH_OK==Th_GetVar(interp, (char *)"::th_stack_trace", -1) ){
899 zStack = Th_TakeResult(interp, &nStack);
900 }
901 Th_ListAppend(interp, &zStack, &nStack, zFirst, zInput-zFirst);
@@ -912,15 +920,15 @@
920 **
921 ** Argument iFrame is interpreted as follows:
922 **
923 ** * If iFrame is 0, this means the current frame.
924 **
925 ** * If iFrame is negative, then the nth frame up the stack, where
926 ** n is the absolute value of iFrame. A value of -1 means the
927 ** calling procedure.
928 **
929 ** * If iFrame is +ve, then the nth frame from the bottom of the
930 ** stack. An iFrame value of 1 means the toplevel (global) frame.
931 */
932 static Th_Frame *getFrame(Th_Interp *interp, int iFrame){
933 Th_Frame *p = interp->pFrame;
934 int i;
@@ -948,28 +956,28 @@
956
957
958 /*
959 ** Evaluate th1 script (zProgram, nProgram) in the frame identified by
960 ** argument iFrame. Leave either an error message or a result in the
961 ** interpreter result and return a th1 error code (TH_OK, TH_ERROR,
962 ** TH_RETURN, TH_CONTINUE or TH_BREAK).
963 */
964 int Th_Eval(Th_Interp *interp, int iFrame, const char *zProgram, int nProgram){
965 int rc = TH_OK;
966 Th_Frame *pSavedFrame = interp->pFrame;
967
968 /* Set Th_Interp.pFrame to the frame that this script is to be
969 ** evaluated in. The current frame is saved in pSavedFrame and will
970 ** be restored before this function returns.
971 */
972 interp->pFrame = getFrame(interp, iFrame);
973
974 if( !interp->pFrame ){
975 rc = TH_ERROR;
976 }else{
977 int nInput = nProgram;
978
979 if( nInput<0 ){
980 nInput = th_strlen(zProgram);
981 }
982 rc = thEvalLocal(interp, zProgram, nInput);
983 }
@@ -995,13 +1003,13 @@
1003 ** array key name.
1004 */
1005 static int thAnalyseVarname(
1006 const char *zVarname,
1007 int nVarname,
1008 const char **pzOuter, /* OUT: Pointer to scalar/array name */
1009 int *pnOuter, /* OUT: Number of bytes at *pzOuter */
1010 const char **pzInner, /* OUT: Pointer to array key (or null) */
1011 int *pnInner, /* OUT: Number of bytes at *pzInner */
1012 int *pisGlobal /* OUT: Set to true if this is a global ref */
1013 ){
1014 const char *zOuter = zVarname;
1015 int nOuter;
@@ -1042,13 +1050,28 @@
1050 *pnInner = nInner;
1051 *pisGlobal = isGlobal;
1052 return TH_OK;
1053 }
1054
1055 /*
1056 ** The Find structure is used to return extra information to callers of the
1057 ** thFindValue function. The fields within it are populated by thFindValue
1058 ** as soon as the necessary information is available. Callers should check
1059 ** each field of interest upon return.
1060 */
1061
1062 struct Find {
1063 Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064 Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065 const char *zElem; /* Name of array element, if applicable */
1066 int nElem; /* Length of array element name, if applicable */
1067 };
1068 typedef struct Find Find;
1069
1070 /*
1071 ** Input string (zVar, nVar) contains a variable name. This function locates
1072 ** the Th_Variable structure associated with the named variable. The
1073 ** variable name may be a global or local scalar or array variable
1074 **
1075 ** If the create argument is non-zero and the named variable does not exist
1076 ** it is created. Otherwise, an error is left in the interpreter result
1077 ** and NULL returned.
@@ -1055,16 +1078,19 @@
1078 **
1079 ** If the arrayok argument is false and the named variable is an array,
1080 ** an error is left in the interpreter result and NULL returned. If
1081 ** arrayok is true an array name is Ok.
1082 */
1083
1084 static Th_Variable *thFindValue(
1085 Th_Interp *interp,
1086 const char *zVar, /* Pointer to variable name */
1087 int nVar, /* Number of bytes at nVar */
1088 int create, /* If true, create the variable if not found */
1089 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090 int noerror, /* If false, set interpreter result to error */
1091 Find *pFind /* If non-zero, place output here */
1092 ){
1093 const char *zOuter;
1094 int nOuter;
1095 const char *zInner;
1096 int nInner;
@@ -1073,16 +1099,24 @@
1099 Th_HashEntry *pEntry;
1100 Th_Frame *pFrame = interp->pFrame;
1101 Th_Variable *pValue;
1102
1103 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104 if( pFind ){
1105 memset(pFind, 0, sizeof(Find));
1106 pFind->zElem = zInner;
1107 pFind->nElem = nInner;
1108 }
1109 if( isGlobal ){
1110 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1111 }
1112
1113 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1114 assert(pEntry || create<=0);
1115 if( pFind ){
1116 pFind->pValueEntry = pEntry;
1117 }
1118 if( !pEntry ){
1119 goto no_such_var;
1120 }
1121
1122 pValue = (Th_Variable *)pEntry->pData;
@@ -1093,20 +1127,26 @@
1127 pEntry->pData = (void *)pValue;
1128 }
1129
1130 if( zInner ){
1131 if( pValue->zData ){
1132 if( !noerror ){
1133 Th_ErrorMessage(interp, "variable is a scalar:", zOuter, nOuter);
1134 }
1135 return 0;
1136 }
1137 if( !pValue->pHash ){
1138 if( !create ){
1139 goto no_such_var;
1140 }
1141 pValue->pHash = Th_HashNew(interp);
1142 }
1143 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144 assert(pEntry || create<=0);
1145 if( pFind ){
1146 pFind->pElemEntry = pEntry;
1147 }
1148 if( !pEntry ){
1149 goto no_such_var;
1150 }
1151 pValue = (Th_Variable *)pEntry->pData;
1152 if( !pValue ){
@@ -1115,34 +1155,38 @@
1155 pValue->nRef = 1;
1156 pEntry->pData = (void *)pValue;
1157 }
1158 }else{
1159 if( pValue->pHash && !arrayok ){
1160 if( !noerror ){
1161 Th_ErrorMessage(interp, "variable is an array:", zOuter, nOuter);
1162 }
1163 return 0;
1164 }
1165 }
1166
1167 return pValue;
1168
1169 no_such_var:
1170 if( !noerror ){
1171 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1172 }
1173 return 0;
1174 }
1175
1176 /*
1177 ** String (zVar, nVar) must contain the name of a scalar variable or
1178 ** array member. Look up the variable, store its current value in
1179 ** the interpreter result and return TH_OK.
1180 **
1181 ** If the named variable does not exist, return TH_ERROR and leave
1182 ** an error message in the interpreter result.
1183 */
1184 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191 if( !pValue->zData ){
1192 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1154,11 +1198,12 @@
1198
1199 /*
1200 ** Return true if variable (zVar, nVar) exists.
1201 */
1202 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1203 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204 return pValue && (pValue->zData || pValue->pHash);
1205 }
1206
1207 /*
1208 ** String (zVar, nVar) must contain the name of a scalar variable or
1209 ** array member. If the variable does not exist it is created. The
@@ -1166,19 +1211,19 @@
1211 **
1212 ** If (zVar, nVar) refers to an existing array, TH_ERROR is returned
1213 ** and an error message left in the interpreter result.
1214 */
1215 int Th_SetVar(
1216 Th_Interp *interp,
1217 const char *zVar,
1218 int nVar,
1219 const char *zValue,
1220 int nValue
1221 ){
1222 Th_Variable *pValue;
1223
1224 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
1225 if( !pValue ){
1226 return TH_ERROR;
1227 }
1228
1229 if( nValue<0 ){
@@ -1202,13 +1247,13 @@
1247 ** Create a variable link so that accessing variable (zLocal, nLocal) is
1248 ** the same as accessing variable (zLink, nLink) in stack frame iFrame.
1249 */
1250 int Th_LinkVar(
1251 Th_Interp *interp, /* Interpreter */
1252 const char *zLocal, int nLocal, /* Local varname */
1253 int iFrame, /* Stack frame of linked var */
1254 const char *zLink, int nLink /* Linked varname */
1255 ){
1256 Th_Frame *pSavedFrame = interp->pFrame;
1257 Th_Frame *pFrame;
1258 Th_HashEntry *pEntry;
1259 Th_Variable *pValue;
@@ -1217,11 +1262,11 @@
1262 if( !pFrame ){
1263 return TH_ERROR;
1264 }
1265 pSavedFrame = interp->pFrame;
1266 interp->pFrame = pFrame;
1267 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
1268 interp->pFrame = pSavedFrame;
1269
1270 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1271 if( pEntry->pData ){
1272 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1238,25 +1283,68 @@
1283 ** an array, or an array member. If the identified variable exists, it
1284 ** is deleted and TH_OK returned. Otherwise, an error message is left
1285 ** in the interpreter result and TH_ERROR is returned.
1286 */
1287 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288 Find find;
1289 Th_Variable *pValue;
1290 Th_HashEntry *pEntry;
1291 int rc = TH_ERROR;
1292
1293 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
1294 if( !pValue ){
1295 return rc;
1296 }
1297
1298 if( pValue->zData || pValue->pHash ){
1299 rc = TH_OK;
1300 }else {
1301 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302 }
1303
1304 /*
1305 ** The variable may be shared by more than one frame; therefore, make sure
1306 ** it is actually freed prior to freeing the parent structure. The values
1307 ** for the variable must be freed now so the variable appears undefined in
1308 ** all frames. The hash entry in the current frame must also be deleted
1309 ** now; otherwise, if the current stack frame is later popped, it will try
1310 ** to delete a variable which has already been freed.
1311 */
1312 if( find.zElem ){
1313 pEntry = find.pElemEntry;
1314 }else{
1315 pEntry = find.pValueEntry;
1316 }
1317 assert( pEntry );
1318 assert( pValue );
1319 if( thFreeVariable(pEntry, (void *)interp) ){
1320 if( find.zElem ){
1321 Th_Variable *pValue2 = find.pValueEntry->pData;
1322 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323 }else if( pEntry->pData ){
1324 Th_Free(interp, pEntry->pData);
1325 pEntry->pData = 0;
1326 }
1327 }else{
1328 if( pValue->zData ){
1329 Th_Free(interp, pValue->zData);
1330 pValue->zData = 0;
1331 }
1332 if( pValue->pHash ){
1333 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334 Th_HashDelete(interp, pValue->pHash);
1335 pValue->pHash = 0;
1336 }
1337 if( find.zElem ){
1338 Th_Variable *pValue2 = find.pValueEntry->pData;
1339 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340 }
1341 }
1342 if( !find.zElem ){
1343 Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344 }
1345 return rc;
1346 }
1347
1348 /*
1349 ** Return an allocated buffer containing a copy of string (z, n). The
1350 ** caller is responsible for eventually calling Th_Free() to free
@@ -1291,11 +1379,11 @@
1379 if( interp ){
1380 char *zRes = 0;
1381 int nRes = 0;
1382
1383 Th_SetVar(interp, (char *)"::th_stack_trace", -1, 0, 0);
1384
1385 Th_StringAppend(interp, &zRes, &nRes, zPre, -1);
1386 if( zRes[nRes-1]=='"' ){
1387 Th_StringAppend(interp, &zRes, &nRes, z, n);
1388 Th_StringAppend(interp, &zRes, &nRes, (const char *)"\"", 1);
1389 }else{
@@ -1373,12 +1461,12 @@
1461 return (char *)Th_Malloc(pInterp, 1);
1462 }
1463 }
1464
1465
1466 /*
1467 ** Wrappers around the supplied malloc() and free()
1468 */
1469 void *Th_Malloc(Th_Interp *pInterp, int nByte){
1470 void *p = pInterp->pVtab->xMalloc(nByte);
1471 if( p ){
1472 memset(p, 0, nByte);
@@ -1390,16 +1478,16 @@
1478 pInterp->pVtab->xFree(z);
1479 }
1480 }
1481
1482 /*
1483 ** Install a new th1 command.
1484 **
1485 ** If a command of the same name already exists, it is deleted automatically.
1486 */
1487 int Th_CreateCommand(
1488 Th_Interp *interp,
1489 const char *zName, /* New command name */
1490 Th_CommandProc xProc, /* Command callback proc */
1491 void *pContext, /* Value to pass as second arg to xProc */
1492 void (*xDel)(Th_Interp *, void *) /* Command destructor callback */
1493 ){
@@ -1417,27 +1505,27 @@
1505 }
1506 pCommand->xProc = xProc;
1507 pCommand->pContext = pContext;
1508 pCommand->xDel = xDel;
1509 pEntry->pData = (void *)pCommand;
1510
1511 return TH_OK;
1512 }
1513
1514 /*
1515 ** Rename the existing command (zName, nName) to (zNew, nNew). If nNew is 0,
1516 ** the command is deleted instead of renamed.
1517 **
1518 ** If successful, TH_OK is returned. If command zName does not exist, or
1519 ** if command zNew already exists, an error message is left in the
1520 ** interpreter result and TH_ERROR is returned.
1521 */
1522 int Th_RenameCommand(
1523 Th_Interp *interp,
1524 const char *zName, /* Existing command name */
1525 int nName, /* Number of bytes at zName */
1526 const char *zNew, /* New command name */
1527 int nNew /* Number of bytes at zNew */
1528 ){
1529 Th_HashEntry *pEntry;
1530 Th_HashEntry *pNewEntry;
1531
@@ -1491,11 +1579,11 @@
1579 ** If an error occurs (if (zList, nList) is not a valid list) an error
1580 ** message is left in the interpreter result and TH_ERROR returned.
1581 **
1582 ** If successful, *pnCount is set to the number of elements in the list.
1583 ** panElem is set to point at an array of *pnCount integers - the lengths
1584 ** of the element values. *pazElem is set to point at an array of
1585 ** pointers to buffers containing the array element's data.
1586 **
1587 ** To free the arrays allocated at *pazElem and *panElem, the caller
1588 ** should call Th_Free() on *pazElem only. Exactly one such call to
1589 ** Th_Free() must be made per call to Th_SplitList().
@@ -1517,13 +1605,13 @@
1605 ** Th_Free(interp, azElem);
1606 **
1607 */
1608 int Th_SplitList(
1609 Th_Interp *interp,
1610 const char *zList, /* Pointer to buffer containing list */
1611 int nList, /* Number of bytes at zList */
1612 char ***pazElem, /* OUT: Array of pointers to element data */
1613 int **panElem, /* OUT: Array of element data lengths */
1614 int *pnCount /* OUT: Number of elements in list */
1615 ){
1616 int rc;
1617 interp->isListMode = 1;
@@ -1534,16 +1622,16 @@
1622 }
1623 return rc;
1624 }
1625
1626 /*
1627 ** Append a new element to an existing th1 list. The element to append
1628 ** to the list is (zElem, nElem).
1629 **
1630 ** A pointer to the existing list must be stored at *pzList when this
1631 ** function is called. The length must be stored in *pnList. The value
1632 ** of *pzList must either be NULL (in which case *pnList must be 0), or
1633 ** a pointer to memory obtained from Th_Malloc().
1634 **
1635 ** This function calls Th_Free() to free the buffer at *pzList and sets
1636 ** *pzList to point to a new buffer containing the new list value. *pnList
1637 ** is similarly updated before returning. The return value is always TH_OK.
@@ -1560,13 +1648,13 @@
1648 ** Th_Free(interp, zList);
1649 **
1650 */
1651 int Th_ListAppend(
1652 Th_Interp *interp, /* Interpreter context */
1653 char **pzList, /* IN/OUT: Ptr to ptr to list */
1654 int *pnList, /* IN/OUT: Current length of *pzList */
1655 const char *zElem, /* Data to append */
1656 int nElem /* Length of nElem */
1657 ){
1658 Buffer output;
1659 int i;
1660
@@ -1615,13 +1703,13 @@
1703 ** Append a new element to an existing th1 string. This function uses
1704 ** the same interface as the Th_ListAppend() function.
1705 */
1706 int Th_StringAppend(
1707 Th_Interp *interp, /* Interpreter context */
1708 char **pzStr, /* IN/OUT: Ptr to ptr to list */
1709 int *pnStr, /* IN/OUT: Current length of *pzStr */
1710 const char *zElem, /* Data to append */
1711 int nElem /* Length of nElem */
1712 ){
1713 char *zNew;
1714 int nNew;
1715
@@ -1639,11 +1727,11 @@
1727 *pnStr = nNew;
1728
1729 return TH_OK;
1730 }
1731
1732 /*
1733 ** Delete an interpreter.
1734 */
1735 void Th_DeleteInterp(Th_Interp *interp){
1736 assert(interp->pFrame);
1737 assert(0==interp->pFrame->pCaller);
@@ -1660,11 +1748,11 @@
1748
1749 /* Delete the interpreter structure itself. */
1750 Th_Free(interp, (void *)interp);
1751 }
1752
1753 /*
1754 ** Create a new interpreter.
1755 */
1756 Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){
1757 Th_Interp *p;
1758
@@ -1694,11 +1782,11 @@
1782 Operator *pOp;
1783 Expr *pParent;
1784 Expr *pLeft;
1785 Expr *pRight;
1786
1787 char *zValue; /* Pointer to literal value */
1788 int nValue; /* Length of literal value buffer */
1789 };
1790
1791 /* Unary operators */
1792 #define OP_UNARY_MINUS 2
@@ -1750,11 +1838,11 @@
1838 {"+", OP_UNARY_PLUS, 1, ARG_NUMBER},
1839 {"~", OP_BITWISE_NOT, 1, ARG_INTEGER},
1840 {"!", OP_LOGICAL_NOT, 1, ARG_INTEGER},
1841
1842 /* Binary operators. It is important to the parsing in Th_Expr() that
1843 * the two-character symbols ("==") appear before the one-character
1844 * ones ("="). And that the priorities of all binary operators are
1845 * integers between 2 and 12.
1846 */
1847 {"<<", OP_LEFTSHIFT, 4, ARG_INTEGER},
1848 {">>", OP_RIGHTSHIFT, 4, ARG_INTEGER},
@@ -1781,16 +1869,16 @@
1869 {0,0,0,0}
1870 };
1871
1872 /*
1873 ** The first part of the string (zInput,nInput) contains a number.
1874 ** Set *pnVarname to the number of bytes in the numeric string.
1875 */
1876 static int thNextNumber(
1877 Th_Interp *interp,
1878 const char *zInput,
1879 int nInput,
1880 int *pnLiteral
1881 ){
1882 int i;
1883 int seenDot = 0;
1884 for(i=0; i<nInput; i++){
@@ -1856,11 +1944,11 @@
1944 if( eArgType==ARG_NUMBER ){
1945 if( (zLeft==0 || TH_OK==Th_ToInt(0, zLeft, nLeft, &iLeft))
1946 && (zRight==0 || TH_OK==Th_ToInt(0, zRight, nRight, &iRight))
1947 ){
1948 eArgType = ARG_INTEGER;
1949 }else if(
1950 (zLeft && TH_OK!=Th_ToDouble(interp, zLeft, nLeft, &fLeft)) ||
1951 (zRight && TH_OK!=Th_ToDouble(interp, zRight, nRight, &fRight))
1952 ){
1953 /* A type error. */
1954 rc = TH_ERROR;
@@ -1868,28 +1956,30 @@
1956 }else if( eArgType==ARG_INTEGER ){
1957 rc = Th_ToInt(interp, zLeft, nLeft, &iLeft);
1958 if( rc==TH_OK && zRight ){
1959 rc = Th_ToInt(interp, zRight, nRight, &iRight);
1960 }
1961 }
1962 }
1963
1964 if( rc==TH_OK && eArgType==ARG_INTEGER ){
1965 int iRes = 0;
1966 switch( pExpr->pOp->eOp ) {
1967 case OP_MULTIPLY: iRes = iLeft*iRight; break;
1968 case OP_DIVIDE:
1969 if( !iRight ){
1970 Th_ErrorMessage(interp, "Divide by 0:", zLeft, nLeft);
1971 rc = TH_ERROR;
1972 goto finish;
1973 }
1974 iRes = iLeft/iRight;
1975 break;
1976 case OP_MODULUS:
1977 if( !iRight ){
1978 Th_ErrorMessage(interp, "Modulo by 0:", zLeft, nLeft);
1979 rc = TH_ERROR;
1980 goto finish;
1981 }
1982 iRes = iLeft%iRight;
1983 break;
1984 case OP_ADD: iRes = iLeft+iRight; break;
1985 case OP_SUBTRACT: iRes = iLeft-iRight; break;
@@ -1913,11 +2003,18 @@
2003 }
2004 Th_SetResultInt(interp, iRes);
2005 }else if( rc==TH_OK && eArgType==ARG_NUMBER ){
2006 switch( pExpr->pOp->eOp ) {
2007 case OP_MULTIPLY: Th_SetResultDouble(interp, fLeft*fRight); break;
2008 case OP_DIVIDE:
2009 if( fRight==0.0 ){
2010 Th_ErrorMessage(interp, "Divide by 0:", zLeft, nLeft);
2011 rc = TH_ERROR;
2012 goto finish;
2013 }
2014 Th_SetResultDouble(interp, fLeft/fRight);
2015 break;
2016 case OP_ADD: Th_SetResultDouble(interp, fLeft+fRight); break;
2017 case OP_SUBTRACT: Th_SetResultDouble(interp, fLeft-fRight); break;
2018 case OP_LT: Th_SetResultInt(interp, fLeft<fRight); break;
2019 case OP_GT: Th_SetResultInt(interp, fLeft>fRight); break;
2020 case OP_LE: Th_SetResultInt(interp, fLeft<=fRight); break;
@@ -1936,10 +2033,12 @@
2033 case OP_SEQ: Th_SetResultInt(interp, iEqual); break;
2034 case OP_SNE: Th_SetResultInt(interp, !iEqual); break;
2035 default: assert(!"Internal error");
2036 }
2037 }
2038
2039 finish:
2040
2041 Th_Free(interp, zLeft);
2042 Th_Free(interp, zRight);
2043 }
2044
@@ -1959,11 +2058,11 @@
2058 #define ISTERM(x) (apToken[x] && (!apToken[x]->pOp || apToken[x]->pLeft))
2059
2060 for(jj=0; jj<nToken; jj++){
2061 if( apToken[jj]->pOp && apToken[jj]->pOp->eOp==OP_OPEN_BRACKET ){
2062 int nNest = 1;
2063 int iLeft = jj;
2064
2065 for(jj++; jj<nToken; jj++){
2066 Operator *pOp = apToken[jj]->pOp;
2067 if( pOp && pOp->eOp==OP_OPEN_BRACKET ) nNest++;
2068 if( pOp && pOp->eOp==OP_CLOSE_BRACKET ) nNest--;
@@ -2033,11 +2132,11 @@
2132 /*
2133 ** Parse a string containing a TH expression to a list of tokens.
2134 */
2135 static int exprParse(
2136 Th_Interp *interp, /* Interpreter to leave error message in */
2137 const char *zExpr, /* Pointer to input string */
2138 int nExpr, /* Number of bytes at zExpr */
2139 Expr ***papToken, /* OUT: Array of tokens. */
2140 int *pnToken /* OUT: Size of token array */
2141 ){
2142 int i;
@@ -2108,11 +2207,11 @@
2207 memcpy(pNew->zValue, z, pNew->nValue);
2208 i += pNew->nValue;
2209 }
2210 if( (nToken%16)==0 ){
2211 /* Grow the apToken array. */
2212 Expr **apTokenOld = apToken;
2213 apToken = Th_Malloc(interp, sizeof(Expr *)*(nToken+16));
2214 memcpy(apToken, apTokenOld, sizeof(Expr *)*nToken);
2215 }
2216
2217 /* Put the new token at the end of the apToken array */
@@ -2133,11 +2232,11 @@
2232 /*
2233 ** Evaluate the string (zExpr, nExpr) as a Th expression. Store
2234 ** the result in the interpreter interp and return TH_OK if
2235 ** successful. If an error occurs, store an error message in
2236 ** the interpreter result and return an error code.
2237 */
2238 int Th_Expr(Th_Interp *interp, const char *zExpr, int nExpr){
2239 int rc; /* Return Code */
2240 int i; /* Loop counter */
2241
2242 int nToken = 0;
@@ -2150,11 +2249,11 @@
2249 /* Parse the expression to a list of tokens. */
2250 rc = exprParse(interp, zExpr, nExpr, &apToken, &nToken);
2251
2252 /* If the parsing was successful, create an expression tree from
2253 ** the parsed list of tokens. If successful, apToken[0] is set
2254 ** to point to the root of the expression tree.
2255 */
2256 if( rc==TH_OK ){
2257 rc = exprMakeTree(interp, apToken, nToken);
2258 }
2259
@@ -2188,16 +2287,17 @@
2287
2288 /*
2289 ** Iterate through all values currently stored in the hash table. Invoke
2290 ** the callback function xCallback for each entry. The second argument
2291 ** passed to xCallback is a copy of the fourth argument passed to this
2292 ** function. The return value from the callback function xCallback is
2293 ** ignored.
2294 */
2295 void Th_HashIterate(
2296 Th_Interp *interp,
2297 Th_Hash *pHash,
2298 int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2299 void *pContext
2300 ){
2301 int i;
2302 for(i=0; i<TH_HASHSIZE; i++){
2303 Th_HashEntry *pEntry;
@@ -2208,14 +2308,15 @@
2308 }
2309 }
2310 }
2311
2312 /*
2313 ** Helper function for Th_HashDelete(). Always returns non-zero.
2314 */
2315 static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2316 Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317 return 1;
2318 }
2319
2320 /*
2321 ** Free a hash-table previously allocated by Th_HashNew().
2322 */
@@ -2225,14 +2326,14 @@
2326 Th_Free(interp, pHash);
2327 }
2328 }
2329
2330 /*
2331 ** This function is used to insert or delete hash table items, or to
2332 ** query a hash table for an existing item.
2333 **
2334 ** If parameter op is less than zero, then the hash-table element
2335 ** identified by (zKey, nKey) is removed from the hash-table if it
2336 ** exists. NULL is returned.
2337 **
2338 ** Otherwise, if the hash-table contains an item with key (zKey, nKey),
2339 ** a pointer to the associated Th_HashEntry is returned. If parameter
@@ -2239,11 +2340,11 @@
2340 ** op is greater than zero, then a new entry is added if one cannot
2341 ** be found. If op is zero, then NULL is returned if the item is
2342 ** not already present in the hash-table.
2343 */
2344 Th_HashEntry *Th_HashFind(
2345 Th_Interp *interp,
2346 Th_Hash *pHash,
2347 const char *zKey,
2348 int nKey,
2349 int op /* -ve = delete, 0 = find, +ve = insert */
2350 ){
@@ -2307,11 +2408,11 @@
2408 ** '\f' 0x0C
2409 ** '\r' 0x0D
2410 **
2411 ** Whitespace characters have the 0x01 flag set. Decimal digits have the
2412 ** 0x2 flag set. Single byte printable characters have the 0x4 flag set.
2413 ** Alphabet characters have the 0x8 bit set.
2414 **
2415 ** The special list characters have the 0x10 flag set
2416 **
2417 ** { } [ ] \ ; ' "
2418 **
@@ -2458,14 +2559,14 @@
2559 return z - zBegin;
2560 }
2561
2562 /*
2563 ** Try to convert the string passed as arguments (z, n) to an integer.
2564 ** If successful, store the result in *piOut and return TH_OK.
2565 **
2566 ** If the string cannot be converted to an integer, return TH_ERROR.
2567 ** If the interp argument is not NULL, leave an error message in the
2568 ** interpreter result too.
2569 */
2570 int Th_ToInt(Th_Interp *interp, const char *z, int n, int *piOut){
2571 int i = 0;
2572 int iOut = 0;
@@ -2493,20 +2594,20 @@
2594 return TH_OK;
2595 }
2596
2597 /*
2598 ** Try to convert the string passed as arguments (z, n) to a double.
2599 ** If successful, store the result in *pfOut and return TH_OK.
2600 **
2601 ** If the string cannot be converted to a double, return TH_ERROR.
2602 ** If the interp argument is not NULL, leave an error message in the
2603 ** interpreter result too.
2604 */
2605 int Th_ToDouble(
2606 Th_Interp *interp,
2607 const char *z,
2608 int n,
2609 double *pfOut
2610 ){
2611 if( !sqlite3IsNumber((const char *)z, 0) ){
2612 Th_ErrorMessage(interp, "expected number, got: \"", z, n);
2613 return TH_ERROR;
@@ -2547,33 +2648,33 @@
2648 ** the double fVal and return TH_OK.
2649 */
2650 int Th_SetResultDouble(Th_Interp *interp, double fVal){
2651 int i; /* Iterator variable */
2652 double v = fVal; /* Input value */
2653 char zBuf[128]; /* Output buffer */
2654 char *z = zBuf; /* Output cursor */
2655 int iDot = 0; /* Digit after which to place decimal point */
2656 int iExp = 0; /* Exponent (NN in eNN) */
2657 const char *zExp; /* String representation of iExp */
2658
2659 /* Precision: */
2660 #define INSIGNIFICANT 0.000000000001
2661 #define ROUNDER 0.0000000000005
2662 double insignificant = INSIGNIFICANT;
2663
2664 /* If the real value is negative, write a '-' character to the
2665 * output and transform v to the corresponding positive number.
2666 */
2667 if( v<0.0 ){
2668 *z++ = '-';
2669 v *= -1.0;
2670 }
2671
2672 /* Normalize v to a value between 1.0 and 10.0. Integer
2673 * variable iExp is set to the exponent. i.e the original
2674 * value is (v * 10^iExp) (or the negative thereof).
2675 */
2676 if( v>0.0 ){
2677 while( (v+ROUNDER)>=10.0 ) { iExp++; v *= 0.1; }
2678 while( (v+ROUNDER)<1.0 ) { iExp--; v *= 10.0; }
2679 }
2680 v += ROUNDER;
2681
+23 -23
--- src/th.h
+++ src/th.h
@@ -18,70 +18,70 @@
1818
/*
1919
** Opaque handle for interpeter.
2020
*/
2121
typedef struct Th_Interp Th_Interp;
2222
23
-/*
24
-** Create and delete interpreters.
23
+/*
24
+** Create and delete interpreters.
2525
*/
2626
Th_Interp * Th_CreateInterp(Th_Vtab *pVtab);
2727
void Th_DeleteInterp(Th_Interp *);
2828
29
-/*
29
+/*
3030
** Evaluate an TH program in the stack frame identified by parameter
3131
** iFrame, according to the following rules:
3232
**
3333
** * If iFrame is 0, this means the current frame.
3434
**
35
-** * If iFrame is negative, then the nth frame up the stack, where n is
35
+** * If iFrame is negative, then the nth frame up the stack, where n is
3636
** the absolute value of iFrame. A value of -1 means the calling
3737
** procedure.
3838
**
3939
** * If iFrame is +ve, then the nth frame from the bottom of the stack.
4040
** An iFrame value of 1 means the toplevel (global) frame.
4141
*/
4242
int Th_Eval(Th_Interp *interp, int iFrame, const char *zProg, int nProg);
4343
4444
/*
45
-** Evaluate a TH expression. The result is stored in the
45
+** Evaluate a TH expression. The result is stored in the
4646
** interpreter result.
4747
*/
4848
int Th_Expr(Th_Interp *interp, const char *, int);
4949
50
-/*
50
+/*
5151
** Access TH variables in the current stack frame. If the variable name
52
-** begins with "::", the lookup is in the top level (global) frame.
52
+** begins with "::", the lookup is in the top level (global) frame.
5353
*/
5454
int Th_ExistsVar(Th_Interp *, const char *, int);
5555
int Th_GetVar(Th_Interp *, const char *, int);
5656
int Th_SetVar(Th_Interp *, const char *, int, const char *, int);
5757
int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int);
5858
int Th_UnsetVar(Th_Interp *, const char *, int);
5959
6060
typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *);
6161
62
-/*
63
-** Register new commands.
62
+/*
63
+** Register new commands.
6464
*/
6565
int Th_CreateCommand(
66
- Th_Interp *interp,
67
- const char *zName,
66
+ Th_Interp *interp,
67
+ const char *zName,
6868
/* int (*xProc)(Th_Interp *, void *, int, const char **, int *), */
6969
Th_CommandProc xProc,
7070
void *pContext,
7171
void (*xDel)(Th_Interp *, void *)
7272
);
7373
74
-/*
74
+/*
7575
** Delete or rename commands.
7676
*/
7777
int Th_RenameCommand(Th_Interp *, const char *, int, const char *, int);
7878
79
-/*
80
-** Push a new stack frame (local variable context) onto the interpreter
81
-** stack, call the function supplied as parameter xCall with the two
82
-** context arguments,
79
+/*
80
+** Push a new stack frame (local variable context) onto the interpreter
81
+** stack, call the function supplied as parameter xCall with the two
82
+** context arguments,
8383
**
8484
** xCall(interp, pContext1, pContext2)
8585
**
8686
** , then pop the frame off of the interpreter stack. The value returned
8787
** by the xCall() function is returned as the result of this function.
@@ -93,21 +93,21 @@
9393
int (*xCall)(Th_Interp *, void *pContext1, void *pContext2),
9494
void *pContext1,
9595
void *pContext2
9696
);
9797
98
-/*
98
+/*
9999
** Valid return codes for xProc callbacks.
100100
*/
101101
#define TH_OK 0
102102
#define TH_ERROR 1
103103
#define TH_BREAK 2
104104
#define TH_RETURN 3
105105
#define TH_CONTINUE 4
106106
107
-/*
108
-** Set and get the interpreter result.
107
+/*
108
+** Set and get the interpreter result.
109109
*/
110110
int Th_SetResult(Th_Interp *, const char *, int);
111111
const char *Th_GetResult(Th_Interp *, int *);
112112
char *Th_TakeResult(Th_Interp *, int *);
113113
@@ -115,26 +115,26 @@
115115
** Set an error message as the interpreter result. This also
116116
** sets the global stack-trace variable $::th_stack_trace.
117117
*/
118118
int Th_ErrorMessage(Th_Interp *, const char *, const char *, int);
119119
120
-/*
120
+/*
121121
** Access the memory management functions associated with the specified
122122
** interpreter.
123123
*/
124124
void *Th_Malloc(Th_Interp *, int);
125125
void Th_Free(Th_Interp *, void *);
126126
127
-/*
127
+/*
128128
** Functions for handling TH lists.
129129
*/
130130
int Th_ListAppend(Th_Interp *, char **, int *, const char *, int);
131131
int Th_SplitList(Th_Interp *, const char *, int, char ***, int **, int *);
132132
133133
int Th_StringAppend(Th_Interp *, char **, int *, const char *, int);
134134
135
-/*
135
+/*
136136
** Functions for handling numbers and pointers.
137137
*/
138138
int Th_ToInt(Th_Interp *, const char *, int, int *);
139139
int Th_ToDouble(Th_Interp *, const char *, int, double *);
140140
int Th_SetResultInt(Th_Interp *, int);
@@ -174,15 +174,15 @@
174174
int nKey;
175175
Th_HashEntry *pNext; /* Internal use only */
176176
};
177177
Th_Hash *Th_HashNew(Th_Interp *);
178178
void Th_HashDelete(Th_Interp *, Th_Hash *);
179
-void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
179
+void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180180
Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181181
182182
/*
183183
** Useful functions from th_lang.c.
184184
*/
185185
int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg);
186186
187187
typedef struct Th_SubCommand {char *zName; Th_CommandProc xProc;} Th_SubCommand;
188188
int Th_CallSubCommand(Th_Interp*,void*,int,const char**,int*,Th_SubCommand*);
189189
--- src/th.h
+++ src/th.h
@@ -18,70 +18,70 @@
18 /*
19 ** Opaque handle for interpeter.
20 */
21 typedef struct Th_Interp Th_Interp;
22
23 /*
24 ** Create and delete interpreters.
25 */
26 Th_Interp * Th_CreateInterp(Th_Vtab *pVtab);
27 void Th_DeleteInterp(Th_Interp *);
28
29 /*
30 ** Evaluate an TH program in the stack frame identified by parameter
31 ** iFrame, according to the following rules:
32 **
33 ** * If iFrame is 0, this means the current frame.
34 **
35 ** * If iFrame is negative, then the nth frame up the stack, where n is
36 ** the absolute value of iFrame. A value of -1 means the calling
37 ** procedure.
38 **
39 ** * If iFrame is +ve, then the nth frame from the bottom of the stack.
40 ** An iFrame value of 1 means the toplevel (global) frame.
41 */
42 int Th_Eval(Th_Interp *interp, int iFrame, const char *zProg, int nProg);
43
44 /*
45 ** Evaluate a TH expression. The result is stored in the
46 ** interpreter result.
47 */
48 int Th_Expr(Th_Interp *interp, const char *, int);
49
50 /*
51 ** Access TH variables in the current stack frame. If the variable name
52 ** begins with "::", the lookup is in the top level (global) frame.
53 */
54 int Th_ExistsVar(Th_Interp *, const char *, int);
55 int Th_GetVar(Th_Interp *, const char *, int);
56 int Th_SetVar(Th_Interp *, const char *, int, const char *, int);
57 int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int);
58 int Th_UnsetVar(Th_Interp *, const char *, int);
59
60 typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *);
61
62 /*
63 ** Register new commands.
64 */
65 int Th_CreateCommand(
66 Th_Interp *interp,
67 const char *zName,
68 /* int (*xProc)(Th_Interp *, void *, int, const char **, int *), */
69 Th_CommandProc xProc,
70 void *pContext,
71 void (*xDel)(Th_Interp *, void *)
72 );
73
74 /*
75 ** Delete or rename commands.
76 */
77 int Th_RenameCommand(Th_Interp *, const char *, int, const char *, int);
78
79 /*
80 ** Push a new stack frame (local variable context) onto the interpreter
81 ** stack, call the function supplied as parameter xCall with the two
82 ** context arguments,
83 **
84 ** xCall(interp, pContext1, pContext2)
85 **
86 ** , then pop the frame off of the interpreter stack. The value returned
87 ** by the xCall() function is returned as the result of this function.
@@ -93,21 +93,21 @@
93 int (*xCall)(Th_Interp *, void *pContext1, void *pContext2),
94 void *pContext1,
95 void *pContext2
96 );
97
98 /*
99 ** Valid return codes for xProc callbacks.
100 */
101 #define TH_OK 0
102 #define TH_ERROR 1
103 #define TH_BREAK 2
104 #define TH_RETURN 3
105 #define TH_CONTINUE 4
106
107 /*
108 ** Set and get the interpreter result.
109 */
110 int Th_SetResult(Th_Interp *, const char *, int);
111 const char *Th_GetResult(Th_Interp *, int *);
112 char *Th_TakeResult(Th_Interp *, int *);
113
@@ -115,26 +115,26 @@
115 ** Set an error message as the interpreter result. This also
116 ** sets the global stack-trace variable $::th_stack_trace.
117 */
118 int Th_ErrorMessage(Th_Interp *, const char *, const char *, int);
119
120 /*
121 ** Access the memory management functions associated with the specified
122 ** interpreter.
123 */
124 void *Th_Malloc(Th_Interp *, int);
125 void Th_Free(Th_Interp *, void *);
126
127 /*
128 ** Functions for handling TH lists.
129 */
130 int Th_ListAppend(Th_Interp *, char **, int *, const char *, int);
131 int Th_SplitList(Th_Interp *, const char *, int, char ***, int **, int *);
132
133 int Th_StringAppend(Th_Interp *, char **, int *, const char *, int);
134
135 /*
136 ** Functions for handling numbers and pointers.
137 */
138 int Th_ToInt(Th_Interp *, const char *, int, int *);
139 int Th_ToDouble(Th_Interp *, const char *, int, double *);
140 int Th_SetResultInt(Th_Interp *, int);
@@ -174,15 +174,15 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185 int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg);
186
187 typedef struct Th_SubCommand {char *zName; Th_CommandProc xProc;} Th_SubCommand;
188 int Th_CallSubCommand(Th_Interp*,void*,int,const char**,int*,Th_SubCommand*);
189
--- src/th.h
+++ src/th.h
@@ -18,70 +18,70 @@
18 /*
19 ** Opaque handle for interpeter.
20 */
21 typedef struct Th_Interp Th_Interp;
22
23 /*
24 ** Create and delete interpreters.
25 */
26 Th_Interp * Th_CreateInterp(Th_Vtab *pVtab);
27 void Th_DeleteInterp(Th_Interp *);
28
29 /*
30 ** Evaluate an TH program in the stack frame identified by parameter
31 ** iFrame, according to the following rules:
32 **
33 ** * If iFrame is 0, this means the current frame.
34 **
35 ** * If iFrame is negative, then the nth frame up the stack, where n is
36 ** the absolute value of iFrame. A value of -1 means the calling
37 ** procedure.
38 **
39 ** * If iFrame is +ve, then the nth frame from the bottom of the stack.
40 ** An iFrame value of 1 means the toplevel (global) frame.
41 */
42 int Th_Eval(Th_Interp *interp, int iFrame, const char *zProg, int nProg);
43
44 /*
45 ** Evaluate a TH expression. The result is stored in the
46 ** interpreter result.
47 */
48 int Th_Expr(Th_Interp *interp, const char *, int);
49
50 /*
51 ** Access TH variables in the current stack frame. If the variable name
52 ** begins with "::", the lookup is in the top level (global) frame.
53 */
54 int Th_ExistsVar(Th_Interp *, const char *, int);
55 int Th_GetVar(Th_Interp *, const char *, int);
56 int Th_SetVar(Th_Interp *, const char *, int, const char *, int);
57 int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int);
58 int Th_UnsetVar(Th_Interp *, const char *, int);
59
60 typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *);
61
62 /*
63 ** Register new commands.
64 */
65 int Th_CreateCommand(
66 Th_Interp *interp,
67 const char *zName,
68 /* int (*xProc)(Th_Interp *, void *, int, const char **, int *), */
69 Th_CommandProc xProc,
70 void *pContext,
71 void (*xDel)(Th_Interp *, void *)
72 );
73
74 /*
75 ** Delete or rename commands.
76 */
77 int Th_RenameCommand(Th_Interp *, const char *, int, const char *, int);
78
79 /*
80 ** Push a new stack frame (local variable context) onto the interpreter
81 ** stack, call the function supplied as parameter xCall with the two
82 ** context arguments,
83 **
84 ** xCall(interp, pContext1, pContext2)
85 **
86 ** , then pop the frame off of the interpreter stack. The value returned
87 ** by the xCall() function is returned as the result of this function.
@@ -93,21 +93,21 @@
93 int (*xCall)(Th_Interp *, void *pContext1, void *pContext2),
94 void *pContext1,
95 void *pContext2
96 );
97
98 /*
99 ** Valid return codes for xProc callbacks.
100 */
101 #define TH_OK 0
102 #define TH_ERROR 1
103 #define TH_BREAK 2
104 #define TH_RETURN 3
105 #define TH_CONTINUE 4
106
107 /*
108 ** Set and get the interpreter result.
109 */
110 int Th_SetResult(Th_Interp *, const char *, int);
111 const char *Th_GetResult(Th_Interp *, int *);
112 char *Th_TakeResult(Th_Interp *, int *);
113
@@ -115,26 +115,26 @@
115 ** Set an error message as the interpreter result. This also
116 ** sets the global stack-trace variable $::th_stack_trace.
117 */
118 int Th_ErrorMessage(Th_Interp *, const char *, const char *, int);
119
120 /*
121 ** Access the memory management functions associated with the specified
122 ** interpreter.
123 */
124 void *Th_Malloc(Th_Interp *, int);
125 void Th_Free(Th_Interp *, void *);
126
127 /*
128 ** Functions for handling TH lists.
129 */
130 int Th_ListAppend(Th_Interp *, char **, int *, const char *, int);
131 int Th_SplitList(Th_Interp *, const char *, int, char ***, int **, int *);
132
133 int Th_StringAppend(Th_Interp *, char **, int *, const char *, int);
134
135 /*
136 ** Functions for handling numbers and pointers.
137 */
138 int Th_ToInt(Th_Interp *, const char *, int, int *);
139 int Th_ToDouble(Th_Interp *, const char *, int, double *);
140 int Th_SetResultInt(Th_Interp *, int);
@@ -174,15 +174,15 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185 int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg);
186
187 typedef struct Th_SubCommand {char *zName; Th_CommandProc xProc;} Th_SubCommand;
188 int Th_CallSubCommand(Th_Interp*,void*,int,const char**,int*,Th_SubCommand*);
189
+113 -113
--- src/th_lang.c
+++ src/th_lang.c
@@ -1,12 +1,12 @@
11
22
/*
3
-** This file contains the implementation of all of the TH language
4
-** built-in commands.
3
+** This file contains the implementation of all of the TH language
4
+** built-in commands.
55
**
6
-** All built-in commands are implemented using the public interface
7
-** declared in th.h, so this file serves as both a part of the language
6
+** All built-in commands are implemented using the public interface
7
+** declared in th.h, so this file serves as both a part of the language
88
** implementation and an example of how to extend the language with
99
** new commands.
1010
*/
1111
1212
#include "config.h"
@@ -18,19 +18,19 @@
1818
Th_ErrorMessage(interp, "wrong # args: should be \"", zMsg, -1);
1919
return TH_ERROR;
2020
}
2121
2222
/*
23
-** Syntax:
23
+** Syntax:
2424
**
2525
** catch script ?varname?
2626
*/
2727
static int catch_command(
28
- Th_Interp *interp,
29
- void *ctx,
30
- int argc,
31
- const char **argv,
28
+ Th_Interp *interp,
29
+ void *ctx,
30
+ int argc,
31
+ const char **argv,
3232
int *argl
3333
){
3434
int rc;
3535
3636
if( argc!=2 && argc!=3 ){
@@ -47,19 +47,19 @@
4747
Th_SetResultInt(interp, rc);
4848
return TH_OK;
4949
}
5050
5151
/*
52
-** TH Syntax:
52
+** TH Syntax:
5353
**
5454
** if expr1 body1 ?elseif expr2 body2? ? ?else? bodyN?
5555
*/
5656
static int if_command(
57
- Th_Interp *interp,
58
- void *ctx,
59
- int argc,
60
- const char **argv,
57
+ Th_Interp *interp,
58
+ void *ctx,
59
+ int argc,
60
+ const char **argv,
6161
int *argl
6262
){
6363
int rc = TH_OK;
6464
6565
int iCond; /* Result of evaluating expression */
@@ -94,19 +94,19 @@
9494
wrong_args:
9595
return Th_WrongNumArgs(interp, "if ...");
9696
}
9797
9898
/*
99
-** TH Syntax:
99
+** TH Syntax:
100100
**
101101
** expr expr
102102
*/
103103
static int expr_command(
104
- Th_Interp *interp,
105
- void *ctx,
106
- int argc,
107
- const char **argv,
104
+ Th_Interp *interp,
105
+ void *ctx,
106
+ int argc,
107
+ const char **argv,
108108
int *argl
109109
){
110110
if( argc!=2 ){
111111
return Th_WrongNumArgs(interp, "expr expression");
112112
}
@@ -113,11 +113,11 @@
113113
114114
return Th_Expr(interp, argv[1], argl[1]);
115115
}
116116
117117
/*
118
-** Evaluate the th1 script (zBody, nBody) in the local stack frame.
118
+** Evaluate the th1 script (zBody, nBody) in the local stack frame.
119119
** Return the result of the evaluation, except if the result
120120
** is TH_CONTINUE, return TH_OK instead.
121121
*/
122122
static int eval_loopbody(Th_Interp *interp, const char *zBody, int nBody){
123123
int rc = Th_Eval(interp, 0, zBody, nBody);
@@ -126,19 +126,19 @@
126126
}
127127
return rc;
128128
}
129129
130130
/*
131
-** TH Syntax:
131
+** TH Syntax:
132132
**
133133
** for init condition incr script
134134
*/
135135
static int for_command(
136
- Th_Interp *interp,
137
- void *ctx,
138
- int argc,
139
- const char **argv,
136
+ Th_Interp *interp,
137
+ void *ctx,
138
+ int argc,
139
+ const char **argv,
140140
int *argl
141141
){
142142
int rc;
143143
int iCond;
144144
@@ -147,11 +147,11 @@
147147
}
148148
149149
/* Evaluate the 'init' script */
150150
rc = Th_Eval(interp, 0, argv[1], -1);
151151
152
- while( rc==TH_OK
152
+ while( rc==TH_OK
153153
&& TH_OK==(rc = Th_Expr(interp, argv[2], -1))
154154
&& TH_OK==(rc = Th_ToInt(interp, Th_GetResult(interp, 0), -1, &iCond))
155155
&& iCond
156156
&& TH_OK==(rc = eval_loopbody(interp, argv[4], argl[4]))
157157
){
@@ -161,45 +161,45 @@
161161
if( rc==TH_BREAK ) rc = TH_OK;
162162
return rc;
163163
}
164164
165165
/*
166
-** TH Syntax:
166
+** TH Syntax:
167167
**
168168
** list ?arg1 ?arg2? ...?
169169
*/
170170
static int list_command(
171
- Th_Interp *interp,
172
- void *ctx,
173
- int argc,
174
- const char **argv,
171
+ Th_Interp *interp,
172
+ void *ctx,
173
+ int argc,
174
+ const char **argv,
175175
int *argl
176176
){
177177
char *zList = 0;
178178
int nList = 0;
179179
int i;
180180
181181
for(i=1; i<argc; i++){
182182
Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
183183
}
184
-
184
+
185185
Th_SetResult(interp, zList, nList);
186186
Th_Free(interp, zList);
187187
188188
return TH_OK;
189189
}
190190
191191
/*
192
-** TH Syntax:
192
+** TH Syntax:
193193
**
194194
** lindex list index
195195
*/
196196
static int lindex_command(
197
- Th_Interp *interp,
198
- void *ctx,
199
- int argc,
200
- const char **argv,
197
+ Th_Interp *interp,
198
+ void *ctx,
199
+ int argc,
200
+ const char **argv,
201201
int *argl
202202
){
203203
int iElem;
204204
int rc;
205205
@@ -227,19 +227,19 @@
227227
228228
return rc;
229229
}
230230
231231
/*
232
-** TH Syntax:
232
+** TH Syntax:
233233
**
234234
** llength list
235235
*/
236236
static int llength_command(
237
- Th_Interp *interp,
238
- void *ctx,
239
- int argc,
240
- const char **argv,
237
+ Th_Interp *interp,
238
+ void *ctx,
239
+ int argc,
240
+ const char **argv,
241241
int *argl
242242
){
243243
int nElem;
244244
int rc;
245245
@@ -254,19 +254,19 @@
254254
255255
return rc;
256256
}
257257
258258
/*
259
-** TH Syntax:
259
+** TH Syntax:
260260
**
261261
** set varname ?value?
262262
*/
263263
static int set_command(
264
- Th_Interp *interp,
265
- void *ctx,
266
- int argc,
267
- const char **argv,
264
+ Th_Interp *interp,
265
+ void *ctx,
266
+ int argc,
267
+ const char **argv,
268268
int *argl
269269
){
270270
if( argc!=2 && argc!=3 ){
271271
return Th_WrongNumArgs(interp, "set varname ?value?");
272272
}
@@ -277,30 +277,30 @@
277277
return Th_GetVar(interp, argv[1], argl[1]);
278278
}
279279
280280
/*
281281
** When a new command is created using the built-in [proc] command, an
282
-** instance of the following structure is allocated and populated. A
283
-** pointer to the structure is passed as the context (second) argument
282
+** instance of the following structure is allocated and populated. A
283
+** pointer to the structure is passed as the context (second) argument
284284
** to function proc_call1() when the new command is executed.
285285
*/
286286
typedef struct ProcDefn ProcDefn;
287287
struct ProcDefn {
288288
int nParam; /* Number of formal (non "args") parameters */
289
- char **azParam; /* Parameter names */
289
+ char **azParam; /* Parameter names */
290290
int *anParam; /* Lengths of parameter names */
291
- char **azDefault; /* Default values */
291
+ char **azDefault; /* Default values */
292292
int *anDefault; /* Lengths of default values */
293293
int hasArgs; /* True if there is an "args" parameter */
294
- char *zProgram; /* Body of proc */
294
+ char *zProgram; /* Body of proc */
295295
int nProgram; /* Number of bytes at zProgram */
296
- char *zUsage; /* Usage message */
296
+ char *zUsage; /* Usage message */
297297
int nUsage; /* Number of bytes at zUsage */
298298
};
299299
300
-/* This structure is used to temporarily store arguments passed to an
301
-** invocation of a command created using [proc]. A pointer to an
300
+/* This structure is used to temporarily store arguments passed to an
301
+** invocation of a command created using [proc]. A pointer to an
302302
** instance is passed as the second argument to the proc_call2() function.
303303
*/
304304
typedef struct ProcArgs ProcArgs;
305305
struct ProcArgs {
306306
int argc;
@@ -323,11 +323,11 @@
323323
ProcArgs *pArgs = (ProcArgs *)pContext2;
324324
325325
/* Check if there are the right number of arguments. If there are
326326
** not, generate a usage message for the command.
327327
*/
328
- if( (pArgs->argc>(p->nParam+1) && !p->hasArgs)
328
+ if( (pArgs->argc>(p->nParam+1) && !p->hasArgs)
329329
|| (pArgs->argc<=(p->nParam) && !p->azDefault[pArgs->argc-1])
330330
){
331331
char *zUsage = 0;
332332
int nUsage = 0;
333333
Th_StringAppend(interp, &zUsage, &nUsage, pArgs->argv[0], pArgs->argl[0]);
@@ -374,12 +374,12 @@
374374
** created using the [proc] command. The second argument, pContext,
375375
** is a pointer to the associated ProcDefn structure.
376376
*/
377377
static int proc_call1(
378378
Th_Interp *interp,
379
- void *pContext,
380
- int argc,
379
+ void *pContext,
380
+ int argc,
381381
const char **argv,
382382
int *argl
383383
){
384384
int rc;
385385
@@ -400,32 +400,32 @@
400400
}
401401
return rc;
402402
}
403403
404404
/*
405
-** This function is registered as the delete callback for all commands
406
-** created using the built-in [proc] command. It is called automatically
407
-** when a command created using [proc] is deleted.
405
+** This function is registered as the delete callback for all commands
406
+** created using the built-in [proc] command. It is called automatically
407
+** when a command created using [proc] is deleted.
408408
**
409409
** It frees the ProcDefn structure allocated when the command was created.
410
-*/
410
+*/
411411
static void proc_del(Th_Interp *interp, void *pContext){
412412
ProcDefn *p = (ProcDefn *)pContext;
413413
Th_Free(interp, (void *)p->zUsage);
414414
Th_Free(interp, (void *)p);
415415
}
416416
417417
/*
418
-** TH Syntax:
418
+** TH Syntax:
419419
**
420420
** proc name arglist code
421421
*/
422422
static int proc_command(
423
- Th_Interp *interp,
424
- void *ctx,
423
+ Th_Interp *interp,
424
+ void *ctx,
425425
int argc,
426
- const char **argv,
426
+ const char **argv,
427427
int *argl
428428
){
429429
int rc;
430430
char *zName;
431431
@@ -436,11 +436,11 @@
436436
437437
char **azParam;
438438
int *anParam;
439439
int nParam;
440440
441
- char *zUsage = 0; /* Build up a usage message here */
441
+ char *zUsage = 0; /* Build up a usage message here */
442442
int nUsage = 0; /* Number of bytes at zUsage */
443443
444444
if( argc!=4 ){
445445
return Th_WrongNumArgs(interp, "proc name arglist code");
446446
}
@@ -448,14 +448,14 @@
448448
return TH_ERROR;
449449
}
450450
451451
/* Allocate the new ProcDefn structure. */
452452
nByte = sizeof(ProcDefn) + /* ProcDefn structure */
453
- (sizeof(char *) + sizeof(int)) * nParam + /* azParam, anParam */
454
- (sizeof(char *) + sizeof(int)) * nParam + /* azDefault, anDefault */
453
+ (sizeof(char *) + sizeof(int)) * nParam + /* azParam, anParam */
454
+ (sizeof(char *) + sizeof(int)) * nParam + /* azDefault, anDefault */
455455
argl[3] + /* zProgram */
456
- argl[2]; /* Space for copies of parameter names and default values */
456
+ argl[2]; /* Space for copies of parameter names and default values */
457457
p = (ProcDefn *)Th_Malloc(interp, nByte);
458458
459459
/* If the last parameter in the parameter list is "args", then set the
460460
** ProcDefn.hasArgs flag. The "args" parameter does not require an
461461
** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays.
@@ -472,11 +472,11 @@
472472
p->anDefault = (int *)&p->azDefault[nParam];
473473
p->zProgram = (char *)&p->anDefault[nParam];
474474
memcpy(p->zProgram, argv[3], argl[3]);
475475
p->nProgram = argl[3];
476476
zSpace = &p->zProgram[p->nProgram];
477
-
477
+
478478
for(i=0; i<nParam; i++){
479479
char **az;
480480
int *an;
481481
int n;
482482
if( Th_SplitList(interp, azParam[i], anParam[i], &az, &an, &n) ){
@@ -537,40 +537,40 @@
537537
Th_Free(interp, zUsage);
538538
return TH_ERROR;
539539
}
540540
541541
/*
542
-** TH Syntax:
542
+** TH Syntax:
543543
**
544544
** rename oldcmd newcmd
545545
*/
546546
static int rename_command(
547
- Th_Interp *interp,
548
- void *ctx,
547
+ Th_Interp *interp,
548
+ void *ctx,
549549
int argc,
550
- const char **argv,
550
+ const char **argv,
551551
int *argl
552552
){
553553
if( argc!=3 ){
554554
return Th_WrongNumArgs(interp, "rename oldcmd newcmd");
555555
}
556556
return Th_RenameCommand(interp, argv[1], argl[1], argv[2], argl[2]);
557557
}
558558
559559
/*
560
-** TH Syntax:
560
+** TH Syntax:
561561
**
562562
** break ?value...?
563563
** continue ?value...?
564564
** ok ?value...?
565565
** error ?value...?
566566
*/
567567
static int simple_command(
568
- Th_Interp *interp,
569
- void *ctx,
570
- int argc,
571
- const char **argv,
568
+ Th_Interp *interp,
569
+ void *ctx,
570
+ int argc,
571
+ const char **argv,
572572
int *argl
573573
){
574574
if( argc!=1 && argc!=2 ){
575575
return Th_WrongNumArgs(interp, "return ?value?");
576576
}
@@ -579,19 +579,19 @@
579579
}
580580
return FOSSIL_PTR_TO_INT(ctx);
581581
}
582582
583583
/*
584
-** TH Syntax:
584
+** TH Syntax:
585585
**
586586
** return ?-code code? ?value?
587587
*/
588588
static int return_command(
589
- Th_Interp *interp,
590
- void *ctx,
591
- int argc,
592
- const char **argv,
589
+ Th_Interp *interp,
590
+ void *ctx,
591
+ int argc,
592
+ const char **argv,
593593
int *argl
594594
){
595595
int iCode = TH_RETURN;
596596
if( argc<1 || argc>4 ){
597597
return Th_WrongNumArgs(interp, "return ?-code code? ?value?");
@@ -638,11 +638,11 @@
638638
iRes = nLeft-nRight;
639639
}
640640
641641
if( iRes<0 ) iRes = -1;
642642
if( iRes>0 ) iRes = 1;
643
-
643
+
644644
return Th_SetResultInt(interp, iRes);
645645
}
646646
647647
/*
648648
** TH Syntax:
@@ -672,11 +672,11 @@
672672
if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673673
iRes = i;
674674
break;
675675
}
676676
}
677
-
677
+
678678
return Th_SetResultInt(interp, iRes);
679679
}
680680
681681
/*
682682
** TH Syntax:
@@ -733,11 +733,11 @@
733733
if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734734
iRes = i;
735735
break;
736736
}
737737
}
738
-
738
+
739739
return Th_SetResultInt(interp, iRes);
740740
}
741741
742742
/*
743743
** TH Syntax:
@@ -867,11 +867,11 @@
867867
** TH Syntax:
868868
**
869869
** unset VAR
870870
*/
871871
static int unset_command(
872
- Th_Interp *interp,
872
+ Th_Interp *interp,
873873
void *ctx,
874874
int argc,
875875
const char **argv,
876876
int *argl
877877
){
@@ -880,11 +880,11 @@
880880
}
881881
return Th_UnsetVar(interp, argv[1], argl[1]);
882882
}
883883
884884
int Th_CallSubCommand(
885
- Th_Interp *interp,
885
+ Th_Interp *interp,
886886
void *ctx,
887887
int argc,
888888
const char **argv,
889889
int *argl,
890890
Th_SubCommand *aSub
@@ -916,11 +916,11 @@
916916
** string length STRING
917917
** string range STRING FIRST LAST
918918
** string repeat STRING COUNT
919919
*/
920920
static int string_command(
921
- Th_Interp *interp,
921
+ Th_Interp *interp,
922922
void *ctx,
923923
int argc,
924924
const char **argv,
925925
int *argl
926926
){
@@ -944,11 +944,11 @@
944944
** TH Syntax:
945945
**
946946
** info exists VARNAME
947947
*/
948948
static int info_command(
949
- Th_Interp *interp,
949
+ Th_Interp *interp,
950950
void *ctx,
951951
int argc,
952952
const char **argv,
953953
int *argl
954954
){
@@ -958,20 +958,20 @@
958958
};
959959
return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub);
960960
}
961961
962962
/*
963
-** Convert the script level frame specification (used by the commands
964
-** [uplevel] and [upvar]) in (zFrame, nFrame) to an integer frame as
963
+** Convert the script level frame specification (used by the commands
964
+** [uplevel] and [upvar]) in (zFrame, nFrame) to an integer frame as
965965
** used by Th_LinkVar() and Th_Eval(). If successful, write the integer
966966
** frame level to *piFrame and return TH_OK. Otherwise, return TH_ERROR
967967
** and leave an error message in the interpreter result.
968968
*/
969969
static int thToFrame(
970
- Th_Interp *interp,
971
- const char *zFrame,
972
- int nFrame,
970
+ Th_Interp *interp,
971
+ const char *zFrame,
972
+ int nFrame,
973973
int *piFrame
974974
){
975975
int iFrame;
976976
if( th_isdigit(zFrame[0]) ){
977977
int rc = Th_ToInt(interp, zFrame, nFrame, &iFrame);
@@ -992,11 +992,11 @@
992992
** TH Syntax:
993993
**
994994
** uplevel ?LEVEL? SCRIPT
995995
*/
996996
static int uplevel_command(
997
- Th_Interp *interp,
997
+ Th_Interp *interp,
998998
void *ctx,
999999
int argc,
10001000
const char **argv,
10011001
int *argl
10021002
){
@@ -1010,19 +1010,19 @@
10101010
}
10111011
return Th_Eval(interp, iFrame, argv[argc-1], -1);
10121012
}
10131013
10141014
/*
1015
-** TH Syntax:
1015
+** TH Syntax:
10161016
**
10171017
** upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR ...?
10181018
*/
10191019
static int upvar_command(
1020
- Th_Interp *interp,
1021
- void *ctx,
1022
- int argc,
1023
- const char **argv,
1020
+ Th_Interp *interp,
1021
+ void *ctx,
1022
+ int argc,
1023
+ const char **argv,
10241024
int *argl
10251025
){
10261026
int iVar = 1;
10271027
int iFrame = -1;
10281028
int rc = TH_OK;
@@ -1030,32 +1030,32 @@
10301030
10311031
if( TH_OK==thToFrame(0, argv[1], argl[1], &iFrame) ){
10321032
iVar++;
10331033
}
10341034
if( argc==iVar || (argc-iVar)%2 ){
1035
- return Th_WrongNumArgs(interp,
1035
+ return Th_WrongNumArgs(interp,
10361036
"upvar frame othervar myvar ?othervar myvar...?");
10371037
}
10381038
for(i=iVar; rc==TH_OK && i<argc; i=i+2){
10391039
rc = Th_LinkVar(interp, argv[i+1], argl[i+1], iFrame, argv[i], argl[i]);
10401040
}
10411041
return rc;
10421042
}
10431043
10441044
/*
1045
-** TH Syntax:
1045
+** TH Syntax:
10461046
**
10471047
** breakpoint ARGS
10481048
**
10491049
** This command does nothing at all. Its purpose in life is to serve
10501050
** as a point for setting breakpoints in a debugger.
10511051
*/
10521052
static int breakpoint_command(
1053
- Th_Interp *interp,
1054
- void *ctx,
1055
- int argc,
1056
- const char **argv,
1053
+ Th_Interp *interp,
1054
+ void *ctx,
1055
+ int argc,
1056
+ const char **argv,
10571057
int *argl
10581058
){
10591059
int cnt = 0;
10601060
cnt++;
10611061
return TH_OK;
@@ -1078,11 +1078,11 @@
10781078
{"if", if_command, 0},
10791079
{"info", info_command, 0},
10801080
{"lindex", lindex_command, 0},
10811081
{"list", list_command, 0},
10821082
{"llength", llength_command, 0},
1083
- {"proc", proc_command, 0},
1083
+ {"proc", proc_command, 0},
10841084
{"rename", rename_command, 0},
10851085
{"set", set_command, 0},
10861086
{"string", string_command, 0},
10871087
{"unset", unset_command, 0},
10881088
{"uplevel", uplevel_command, 0},
@@ -1089,13 +1089,13 @@
10891089
{"upvar", upvar_command, 0},
10901090
10911091
{"breakpoint", breakpoint_command, 0},
10921092
10931093
{"return", return_command, 0},
1094
- {"break", simple_command, (void *)TH_BREAK},
1095
- {"continue", simple_command, (void *)TH_CONTINUE},
1096
- {"error", simple_command, (void *)TH_ERROR},
1094
+ {"break", simple_command, (void *)TH_BREAK},
1095
+ {"continue", simple_command, (void *)TH_CONTINUE},
1096
+ {"error", simple_command, (void *)TH_ERROR},
10971097
10981098
{0, 0, 0}
10991099
};
11001100
int i;
11011101
11021102
--- src/th_lang.c
+++ src/th_lang.c
@@ -1,12 +1,12 @@
1
2 /*
3 ** This file contains the implementation of all of the TH language
4 ** built-in commands.
5 **
6 ** All built-in commands are implemented using the public interface
7 ** declared in th.h, so this file serves as both a part of the language
8 ** implementation and an example of how to extend the language with
9 ** new commands.
10 */
11
12 #include "config.h"
@@ -18,19 +18,19 @@
18 Th_ErrorMessage(interp, "wrong # args: should be \"", zMsg, -1);
19 return TH_ERROR;
20 }
21
22 /*
23 ** Syntax:
24 **
25 ** catch script ?varname?
26 */
27 static int catch_command(
28 Th_Interp *interp,
29 void *ctx,
30 int argc,
31 const char **argv,
32 int *argl
33 ){
34 int rc;
35
36 if( argc!=2 && argc!=3 ){
@@ -47,19 +47,19 @@
47 Th_SetResultInt(interp, rc);
48 return TH_OK;
49 }
50
51 /*
52 ** TH Syntax:
53 **
54 ** if expr1 body1 ?elseif expr2 body2? ? ?else? bodyN?
55 */
56 static int if_command(
57 Th_Interp *interp,
58 void *ctx,
59 int argc,
60 const char **argv,
61 int *argl
62 ){
63 int rc = TH_OK;
64
65 int iCond; /* Result of evaluating expression */
@@ -94,19 +94,19 @@
94 wrong_args:
95 return Th_WrongNumArgs(interp, "if ...");
96 }
97
98 /*
99 ** TH Syntax:
100 **
101 ** expr expr
102 */
103 static int expr_command(
104 Th_Interp *interp,
105 void *ctx,
106 int argc,
107 const char **argv,
108 int *argl
109 ){
110 if( argc!=2 ){
111 return Th_WrongNumArgs(interp, "expr expression");
112 }
@@ -113,11 +113,11 @@
113
114 return Th_Expr(interp, argv[1], argl[1]);
115 }
116
117 /*
118 ** Evaluate the th1 script (zBody, nBody) in the local stack frame.
119 ** Return the result of the evaluation, except if the result
120 ** is TH_CONTINUE, return TH_OK instead.
121 */
122 static int eval_loopbody(Th_Interp *interp, const char *zBody, int nBody){
123 int rc = Th_Eval(interp, 0, zBody, nBody);
@@ -126,19 +126,19 @@
126 }
127 return rc;
128 }
129
130 /*
131 ** TH Syntax:
132 **
133 ** for init condition incr script
134 */
135 static int for_command(
136 Th_Interp *interp,
137 void *ctx,
138 int argc,
139 const char **argv,
140 int *argl
141 ){
142 int rc;
143 int iCond;
144
@@ -147,11 +147,11 @@
147 }
148
149 /* Evaluate the 'init' script */
150 rc = Th_Eval(interp, 0, argv[1], -1);
151
152 while( rc==TH_OK
153 && TH_OK==(rc = Th_Expr(interp, argv[2], -1))
154 && TH_OK==(rc = Th_ToInt(interp, Th_GetResult(interp, 0), -1, &iCond))
155 && iCond
156 && TH_OK==(rc = eval_loopbody(interp, argv[4], argl[4]))
157 ){
@@ -161,45 +161,45 @@
161 if( rc==TH_BREAK ) rc = TH_OK;
162 return rc;
163 }
164
165 /*
166 ** TH Syntax:
167 **
168 ** list ?arg1 ?arg2? ...?
169 */
170 static int list_command(
171 Th_Interp *interp,
172 void *ctx,
173 int argc,
174 const char **argv,
175 int *argl
176 ){
177 char *zList = 0;
178 int nList = 0;
179 int i;
180
181 for(i=1; i<argc; i++){
182 Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
183 }
184
185 Th_SetResult(interp, zList, nList);
186 Th_Free(interp, zList);
187
188 return TH_OK;
189 }
190
191 /*
192 ** TH Syntax:
193 **
194 ** lindex list index
195 */
196 static int lindex_command(
197 Th_Interp *interp,
198 void *ctx,
199 int argc,
200 const char **argv,
201 int *argl
202 ){
203 int iElem;
204 int rc;
205
@@ -227,19 +227,19 @@
227
228 return rc;
229 }
230
231 /*
232 ** TH Syntax:
233 **
234 ** llength list
235 */
236 static int llength_command(
237 Th_Interp *interp,
238 void *ctx,
239 int argc,
240 const char **argv,
241 int *argl
242 ){
243 int nElem;
244 int rc;
245
@@ -254,19 +254,19 @@
254
255 return rc;
256 }
257
258 /*
259 ** TH Syntax:
260 **
261 ** set varname ?value?
262 */
263 static int set_command(
264 Th_Interp *interp,
265 void *ctx,
266 int argc,
267 const char **argv,
268 int *argl
269 ){
270 if( argc!=2 && argc!=3 ){
271 return Th_WrongNumArgs(interp, "set varname ?value?");
272 }
@@ -277,30 +277,30 @@
277 return Th_GetVar(interp, argv[1], argl[1]);
278 }
279
280 /*
281 ** When a new command is created using the built-in [proc] command, an
282 ** instance of the following structure is allocated and populated. A
283 ** pointer to the structure is passed as the context (second) argument
284 ** to function proc_call1() when the new command is executed.
285 */
286 typedef struct ProcDefn ProcDefn;
287 struct ProcDefn {
288 int nParam; /* Number of formal (non "args") parameters */
289 char **azParam; /* Parameter names */
290 int *anParam; /* Lengths of parameter names */
291 char **azDefault; /* Default values */
292 int *anDefault; /* Lengths of default values */
293 int hasArgs; /* True if there is an "args" parameter */
294 char *zProgram; /* Body of proc */
295 int nProgram; /* Number of bytes at zProgram */
296 char *zUsage; /* Usage message */
297 int nUsage; /* Number of bytes at zUsage */
298 };
299
300 /* This structure is used to temporarily store arguments passed to an
301 ** invocation of a command created using [proc]. A pointer to an
302 ** instance is passed as the second argument to the proc_call2() function.
303 */
304 typedef struct ProcArgs ProcArgs;
305 struct ProcArgs {
306 int argc;
@@ -323,11 +323,11 @@
323 ProcArgs *pArgs = (ProcArgs *)pContext2;
324
325 /* Check if there are the right number of arguments. If there are
326 ** not, generate a usage message for the command.
327 */
328 if( (pArgs->argc>(p->nParam+1) && !p->hasArgs)
329 || (pArgs->argc<=(p->nParam) && !p->azDefault[pArgs->argc-1])
330 ){
331 char *zUsage = 0;
332 int nUsage = 0;
333 Th_StringAppend(interp, &zUsage, &nUsage, pArgs->argv[0], pArgs->argl[0]);
@@ -374,12 +374,12 @@
374 ** created using the [proc] command. The second argument, pContext,
375 ** is a pointer to the associated ProcDefn structure.
376 */
377 static int proc_call1(
378 Th_Interp *interp,
379 void *pContext,
380 int argc,
381 const char **argv,
382 int *argl
383 ){
384 int rc;
385
@@ -400,32 +400,32 @@
400 }
401 return rc;
402 }
403
404 /*
405 ** This function is registered as the delete callback for all commands
406 ** created using the built-in [proc] command. It is called automatically
407 ** when a command created using [proc] is deleted.
408 **
409 ** It frees the ProcDefn structure allocated when the command was created.
410 */
411 static void proc_del(Th_Interp *interp, void *pContext){
412 ProcDefn *p = (ProcDefn *)pContext;
413 Th_Free(interp, (void *)p->zUsage);
414 Th_Free(interp, (void *)p);
415 }
416
417 /*
418 ** TH Syntax:
419 **
420 ** proc name arglist code
421 */
422 static int proc_command(
423 Th_Interp *interp,
424 void *ctx,
425 int argc,
426 const char **argv,
427 int *argl
428 ){
429 int rc;
430 char *zName;
431
@@ -436,11 +436,11 @@
436
437 char **azParam;
438 int *anParam;
439 int nParam;
440
441 char *zUsage = 0; /* Build up a usage message here */
442 int nUsage = 0; /* Number of bytes at zUsage */
443
444 if( argc!=4 ){
445 return Th_WrongNumArgs(interp, "proc name arglist code");
446 }
@@ -448,14 +448,14 @@
448 return TH_ERROR;
449 }
450
451 /* Allocate the new ProcDefn structure. */
452 nByte = sizeof(ProcDefn) + /* ProcDefn structure */
453 (sizeof(char *) + sizeof(int)) * nParam + /* azParam, anParam */
454 (sizeof(char *) + sizeof(int)) * nParam + /* azDefault, anDefault */
455 argl[3] + /* zProgram */
456 argl[2]; /* Space for copies of parameter names and default values */
457 p = (ProcDefn *)Th_Malloc(interp, nByte);
458
459 /* If the last parameter in the parameter list is "args", then set the
460 ** ProcDefn.hasArgs flag. The "args" parameter does not require an
461 ** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays.
@@ -472,11 +472,11 @@
472 p->anDefault = (int *)&p->azDefault[nParam];
473 p->zProgram = (char *)&p->anDefault[nParam];
474 memcpy(p->zProgram, argv[3], argl[3]);
475 p->nProgram = argl[3];
476 zSpace = &p->zProgram[p->nProgram];
477
478 for(i=0; i<nParam; i++){
479 char **az;
480 int *an;
481 int n;
482 if( Th_SplitList(interp, azParam[i], anParam[i], &az, &an, &n) ){
@@ -537,40 +537,40 @@
537 Th_Free(interp, zUsage);
538 return TH_ERROR;
539 }
540
541 /*
542 ** TH Syntax:
543 **
544 ** rename oldcmd newcmd
545 */
546 static int rename_command(
547 Th_Interp *interp,
548 void *ctx,
549 int argc,
550 const char **argv,
551 int *argl
552 ){
553 if( argc!=3 ){
554 return Th_WrongNumArgs(interp, "rename oldcmd newcmd");
555 }
556 return Th_RenameCommand(interp, argv[1], argl[1], argv[2], argl[2]);
557 }
558
559 /*
560 ** TH Syntax:
561 **
562 ** break ?value...?
563 ** continue ?value...?
564 ** ok ?value...?
565 ** error ?value...?
566 */
567 static int simple_command(
568 Th_Interp *interp,
569 void *ctx,
570 int argc,
571 const char **argv,
572 int *argl
573 ){
574 if( argc!=1 && argc!=2 ){
575 return Th_WrongNumArgs(interp, "return ?value?");
576 }
@@ -579,19 +579,19 @@
579 }
580 return FOSSIL_PTR_TO_INT(ctx);
581 }
582
583 /*
584 ** TH Syntax:
585 **
586 ** return ?-code code? ?value?
587 */
588 static int return_command(
589 Th_Interp *interp,
590 void *ctx,
591 int argc,
592 const char **argv,
593 int *argl
594 ){
595 int iCode = TH_RETURN;
596 if( argc<1 || argc>4 ){
597 return Th_WrongNumArgs(interp, "return ?-code code? ?value?");
@@ -638,11 +638,11 @@
638 iRes = nLeft-nRight;
639 }
640
641 if( iRes<0 ) iRes = -1;
642 if( iRes>0 ) iRes = 1;
643
644 return Th_SetResultInt(interp, iRes);
645 }
646
647 /*
648 ** TH Syntax:
@@ -672,11 +672,11 @@
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
675 }
676 }
677
678 return Th_SetResultInt(interp, iRes);
679 }
680
681 /*
682 ** TH Syntax:
@@ -733,11 +733,11 @@
733 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734 iRes = i;
735 break;
736 }
737 }
738
739 return Th_SetResultInt(interp, iRes);
740 }
741
742 /*
743 ** TH Syntax:
@@ -867,11 +867,11 @@
867 ** TH Syntax:
868 **
869 ** unset VAR
870 */
871 static int unset_command(
872 Th_Interp *interp,
873 void *ctx,
874 int argc,
875 const char **argv,
876 int *argl
877 ){
@@ -880,11 +880,11 @@
880 }
881 return Th_UnsetVar(interp, argv[1], argl[1]);
882 }
883
884 int Th_CallSubCommand(
885 Th_Interp *interp,
886 void *ctx,
887 int argc,
888 const char **argv,
889 int *argl,
890 Th_SubCommand *aSub
@@ -916,11 +916,11 @@
916 ** string length STRING
917 ** string range STRING FIRST LAST
918 ** string repeat STRING COUNT
919 */
920 static int string_command(
921 Th_Interp *interp,
922 void *ctx,
923 int argc,
924 const char **argv,
925 int *argl
926 ){
@@ -944,11 +944,11 @@
944 ** TH Syntax:
945 **
946 ** info exists VARNAME
947 */
948 static int info_command(
949 Th_Interp *interp,
950 void *ctx,
951 int argc,
952 const char **argv,
953 int *argl
954 ){
@@ -958,20 +958,20 @@
958 };
959 return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub);
960 }
961
962 /*
963 ** Convert the script level frame specification (used by the commands
964 ** [uplevel] and [upvar]) in (zFrame, nFrame) to an integer frame as
965 ** used by Th_LinkVar() and Th_Eval(). If successful, write the integer
966 ** frame level to *piFrame and return TH_OK. Otherwise, return TH_ERROR
967 ** and leave an error message in the interpreter result.
968 */
969 static int thToFrame(
970 Th_Interp *interp,
971 const char *zFrame,
972 int nFrame,
973 int *piFrame
974 ){
975 int iFrame;
976 if( th_isdigit(zFrame[0]) ){
977 int rc = Th_ToInt(interp, zFrame, nFrame, &iFrame);
@@ -992,11 +992,11 @@
992 ** TH Syntax:
993 **
994 ** uplevel ?LEVEL? SCRIPT
995 */
996 static int uplevel_command(
997 Th_Interp *interp,
998 void *ctx,
999 int argc,
1000 const char **argv,
1001 int *argl
1002 ){
@@ -1010,19 +1010,19 @@
1010 }
1011 return Th_Eval(interp, iFrame, argv[argc-1], -1);
1012 }
1013
1014 /*
1015 ** TH Syntax:
1016 **
1017 ** upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR ...?
1018 */
1019 static int upvar_command(
1020 Th_Interp *interp,
1021 void *ctx,
1022 int argc,
1023 const char **argv,
1024 int *argl
1025 ){
1026 int iVar = 1;
1027 int iFrame = -1;
1028 int rc = TH_OK;
@@ -1030,32 +1030,32 @@
1030
1031 if( TH_OK==thToFrame(0, argv[1], argl[1], &iFrame) ){
1032 iVar++;
1033 }
1034 if( argc==iVar || (argc-iVar)%2 ){
1035 return Th_WrongNumArgs(interp,
1036 "upvar frame othervar myvar ?othervar myvar...?");
1037 }
1038 for(i=iVar; rc==TH_OK && i<argc; i=i+2){
1039 rc = Th_LinkVar(interp, argv[i+1], argl[i+1], iFrame, argv[i], argl[i]);
1040 }
1041 return rc;
1042 }
1043
1044 /*
1045 ** TH Syntax:
1046 **
1047 ** breakpoint ARGS
1048 **
1049 ** This command does nothing at all. Its purpose in life is to serve
1050 ** as a point for setting breakpoints in a debugger.
1051 */
1052 static int breakpoint_command(
1053 Th_Interp *interp,
1054 void *ctx,
1055 int argc,
1056 const char **argv,
1057 int *argl
1058 ){
1059 int cnt = 0;
1060 cnt++;
1061 return TH_OK;
@@ -1078,11 +1078,11 @@
1078 {"if", if_command, 0},
1079 {"info", info_command, 0},
1080 {"lindex", lindex_command, 0},
1081 {"list", list_command, 0},
1082 {"llength", llength_command, 0},
1083 {"proc", proc_command, 0},
1084 {"rename", rename_command, 0},
1085 {"set", set_command, 0},
1086 {"string", string_command, 0},
1087 {"unset", unset_command, 0},
1088 {"uplevel", uplevel_command, 0},
@@ -1089,13 +1089,13 @@
1089 {"upvar", upvar_command, 0},
1090
1091 {"breakpoint", breakpoint_command, 0},
1092
1093 {"return", return_command, 0},
1094 {"break", simple_command, (void *)TH_BREAK},
1095 {"continue", simple_command, (void *)TH_CONTINUE},
1096 {"error", simple_command, (void *)TH_ERROR},
1097
1098 {0, 0, 0}
1099 };
1100 int i;
1101
1102
--- src/th_lang.c
+++ src/th_lang.c
@@ -1,12 +1,12 @@
1
2 /*
3 ** This file contains the implementation of all of the TH language
4 ** built-in commands.
5 **
6 ** All built-in commands are implemented using the public interface
7 ** declared in th.h, so this file serves as both a part of the language
8 ** implementation and an example of how to extend the language with
9 ** new commands.
10 */
11
12 #include "config.h"
@@ -18,19 +18,19 @@
18 Th_ErrorMessage(interp, "wrong # args: should be \"", zMsg, -1);
19 return TH_ERROR;
20 }
21
22 /*
23 ** Syntax:
24 **
25 ** catch script ?varname?
26 */
27 static int catch_command(
28 Th_Interp *interp,
29 void *ctx,
30 int argc,
31 const char **argv,
32 int *argl
33 ){
34 int rc;
35
36 if( argc!=2 && argc!=3 ){
@@ -47,19 +47,19 @@
47 Th_SetResultInt(interp, rc);
48 return TH_OK;
49 }
50
51 /*
52 ** TH Syntax:
53 **
54 ** if expr1 body1 ?elseif expr2 body2? ? ?else? bodyN?
55 */
56 static int if_command(
57 Th_Interp *interp,
58 void *ctx,
59 int argc,
60 const char **argv,
61 int *argl
62 ){
63 int rc = TH_OK;
64
65 int iCond; /* Result of evaluating expression */
@@ -94,19 +94,19 @@
94 wrong_args:
95 return Th_WrongNumArgs(interp, "if ...");
96 }
97
98 /*
99 ** TH Syntax:
100 **
101 ** expr expr
102 */
103 static int expr_command(
104 Th_Interp *interp,
105 void *ctx,
106 int argc,
107 const char **argv,
108 int *argl
109 ){
110 if( argc!=2 ){
111 return Th_WrongNumArgs(interp, "expr expression");
112 }
@@ -113,11 +113,11 @@
113
114 return Th_Expr(interp, argv[1], argl[1]);
115 }
116
117 /*
118 ** Evaluate the th1 script (zBody, nBody) in the local stack frame.
119 ** Return the result of the evaluation, except if the result
120 ** is TH_CONTINUE, return TH_OK instead.
121 */
122 static int eval_loopbody(Th_Interp *interp, const char *zBody, int nBody){
123 int rc = Th_Eval(interp, 0, zBody, nBody);
@@ -126,19 +126,19 @@
126 }
127 return rc;
128 }
129
130 /*
131 ** TH Syntax:
132 **
133 ** for init condition incr script
134 */
135 static int for_command(
136 Th_Interp *interp,
137 void *ctx,
138 int argc,
139 const char **argv,
140 int *argl
141 ){
142 int rc;
143 int iCond;
144
@@ -147,11 +147,11 @@
147 }
148
149 /* Evaluate the 'init' script */
150 rc = Th_Eval(interp, 0, argv[1], -1);
151
152 while( rc==TH_OK
153 && TH_OK==(rc = Th_Expr(interp, argv[2], -1))
154 && TH_OK==(rc = Th_ToInt(interp, Th_GetResult(interp, 0), -1, &iCond))
155 && iCond
156 && TH_OK==(rc = eval_loopbody(interp, argv[4], argl[4]))
157 ){
@@ -161,45 +161,45 @@
161 if( rc==TH_BREAK ) rc = TH_OK;
162 return rc;
163 }
164
165 /*
166 ** TH Syntax:
167 **
168 ** list ?arg1 ?arg2? ...?
169 */
170 static int list_command(
171 Th_Interp *interp,
172 void *ctx,
173 int argc,
174 const char **argv,
175 int *argl
176 ){
177 char *zList = 0;
178 int nList = 0;
179 int i;
180
181 for(i=1; i<argc; i++){
182 Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
183 }
184
185 Th_SetResult(interp, zList, nList);
186 Th_Free(interp, zList);
187
188 return TH_OK;
189 }
190
191 /*
192 ** TH Syntax:
193 **
194 ** lindex list index
195 */
196 static int lindex_command(
197 Th_Interp *interp,
198 void *ctx,
199 int argc,
200 const char **argv,
201 int *argl
202 ){
203 int iElem;
204 int rc;
205
@@ -227,19 +227,19 @@
227
228 return rc;
229 }
230
231 /*
232 ** TH Syntax:
233 **
234 ** llength list
235 */
236 static int llength_command(
237 Th_Interp *interp,
238 void *ctx,
239 int argc,
240 const char **argv,
241 int *argl
242 ){
243 int nElem;
244 int rc;
245
@@ -254,19 +254,19 @@
254
255 return rc;
256 }
257
258 /*
259 ** TH Syntax:
260 **
261 ** set varname ?value?
262 */
263 static int set_command(
264 Th_Interp *interp,
265 void *ctx,
266 int argc,
267 const char **argv,
268 int *argl
269 ){
270 if( argc!=2 && argc!=3 ){
271 return Th_WrongNumArgs(interp, "set varname ?value?");
272 }
@@ -277,30 +277,30 @@
277 return Th_GetVar(interp, argv[1], argl[1]);
278 }
279
280 /*
281 ** When a new command is created using the built-in [proc] command, an
282 ** instance of the following structure is allocated and populated. A
283 ** pointer to the structure is passed as the context (second) argument
284 ** to function proc_call1() when the new command is executed.
285 */
286 typedef struct ProcDefn ProcDefn;
287 struct ProcDefn {
288 int nParam; /* Number of formal (non "args") parameters */
289 char **azParam; /* Parameter names */
290 int *anParam; /* Lengths of parameter names */
291 char **azDefault; /* Default values */
292 int *anDefault; /* Lengths of default values */
293 int hasArgs; /* True if there is an "args" parameter */
294 char *zProgram; /* Body of proc */
295 int nProgram; /* Number of bytes at zProgram */
296 char *zUsage; /* Usage message */
297 int nUsage; /* Number of bytes at zUsage */
298 };
299
300 /* This structure is used to temporarily store arguments passed to an
301 ** invocation of a command created using [proc]. A pointer to an
302 ** instance is passed as the second argument to the proc_call2() function.
303 */
304 typedef struct ProcArgs ProcArgs;
305 struct ProcArgs {
306 int argc;
@@ -323,11 +323,11 @@
323 ProcArgs *pArgs = (ProcArgs *)pContext2;
324
325 /* Check if there are the right number of arguments. If there are
326 ** not, generate a usage message for the command.
327 */
328 if( (pArgs->argc>(p->nParam+1) && !p->hasArgs)
329 || (pArgs->argc<=(p->nParam) && !p->azDefault[pArgs->argc-1])
330 ){
331 char *zUsage = 0;
332 int nUsage = 0;
333 Th_StringAppend(interp, &zUsage, &nUsage, pArgs->argv[0], pArgs->argl[0]);
@@ -374,12 +374,12 @@
374 ** created using the [proc] command. The second argument, pContext,
375 ** is a pointer to the associated ProcDefn structure.
376 */
377 static int proc_call1(
378 Th_Interp *interp,
379 void *pContext,
380 int argc,
381 const char **argv,
382 int *argl
383 ){
384 int rc;
385
@@ -400,32 +400,32 @@
400 }
401 return rc;
402 }
403
404 /*
405 ** This function is registered as the delete callback for all commands
406 ** created using the built-in [proc] command. It is called automatically
407 ** when a command created using [proc] is deleted.
408 **
409 ** It frees the ProcDefn structure allocated when the command was created.
410 */
411 static void proc_del(Th_Interp *interp, void *pContext){
412 ProcDefn *p = (ProcDefn *)pContext;
413 Th_Free(interp, (void *)p->zUsage);
414 Th_Free(interp, (void *)p);
415 }
416
417 /*
418 ** TH Syntax:
419 **
420 ** proc name arglist code
421 */
422 static int proc_command(
423 Th_Interp *interp,
424 void *ctx,
425 int argc,
426 const char **argv,
427 int *argl
428 ){
429 int rc;
430 char *zName;
431
@@ -436,11 +436,11 @@
436
437 char **azParam;
438 int *anParam;
439 int nParam;
440
441 char *zUsage = 0; /* Build up a usage message here */
442 int nUsage = 0; /* Number of bytes at zUsage */
443
444 if( argc!=4 ){
445 return Th_WrongNumArgs(interp, "proc name arglist code");
446 }
@@ -448,14 +448,14 @@
448 return TH_ERROR;
449 }
450
451 /* Allocate the new ProcDefn structure. */
452 nByte = sizeof(ProcDefn) + /* ProcDefn structure */
453 (sizeof(char *) + sizeof(int)) * nParam + /* azParam, anParam */
454 (sizeof(char *) + sizeof(int)) * nParam + /* azDefault, anDefault */
455 argl[3] + /* zProgram */
456 argl[2]; /* Space for copies of parameter names and default values */
457 p = (ProcDefn *)Th_Malloc(interp, nByte);
458
459 /* If the last parameter in the parameter list is "args", then set the
460 ** ProcDefn.hasArgs flag. The "args" parameter does not require an
461 ** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays.
@@ -472,11 +472,11 @@
472 p->anDefault = (int *)&p->azDefault[nParam];
473 p->zProgram = (char *)&p->anDefault[nParam];
474 memcpy(p->zProgram, argv[3], argl[3]);
475 p->nProgram = argl[3];
476 zSpace = &p->zProgram[p->nProgram];
477
478 for(i=0; i<nParam; i++){
479 char **az;
480 int *an;
481 int n;
482 if( Th_SplitList(interp, azParam[i], anParam[i], &az, &an, &n) ){
@@ -537,40 +537,40 @@
537 Th_Free(interp, zUsage);
538 return TH_ERROR;
539 }
540
541 /*
542 ** TH Syntax:
543 **
544 ** rename oldcmd newcmd
545 */
546 static int rename_command(
547 Th_Interp *interp,
548 void *ctx,
549 int argc,
550 const char **argv,
551 int *argl
552 ){
553 if( argc!=3 ){
554 return Th_WrongNumArgs(interp, "rename oldcmd newcmd");
555 }
556 return Th_RenameCommand(interp, argv[1], argl[1], argv[2], argl[2]);
557 }
558
559 /*
560 ** TH Syntax:
561 **
562 ** break ?value...?
563 ** continue ?value...?
564 ** ok ?value...?
565 ** error ?value...?
566 */
567 static int simple_command(
568 Th_Interp *interp,
569 void *ctx,
570 int argc,
571 const char **argv,
572 int *argl
573 ){
574 if( argc!=1 && argc!=2 ){
575 return Th_WrongNumArgs(interp, "return ?value?");
576 }
@@ -579,19 +579,19 @@
579 }
580 return FOSSIL_PTR_TO_INT(ctx);
581 }
582
583 /*
584 ** TH Syntax:
585 **
586 ** return ?-code code? ?value?
587 */
588 static int return_command(
589 Th_Interp *interp,
590 void *ctx,
591 int argc,
592 const char **argv,
593 int *argl
594 ){
595 int iCode = TH_RETURN;
596 if( argc<1 || argc>4 ){
597 return Th_WrongNumArgs(interp, "return ?-code code? ?value?");
@@ -638,11 +638,11 @@
638 iRes = nLeft-nRight;
639 }
640
641 if( iRes<0 ) iRes = -1;
642 if( iRes>0 ) iRes = 1;
643
644 return Th_SetResultInt(interp, iRes);
645 }
646
647 /*
648 ** TH Syntax:
@@ -672,11 +672,11 @@
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
675 }
676 }
677
678 return Th_SetResultInt(interp, iRes);
679 }
680
681 /*
682 ** TH Syntax:
@@ -733,11 +733,11 @@
733 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734 iRes = i;
735 break;
736 }
737 }
738
739 return Th_SetResultInt(interp, iRes);
740 }
741
742 /*
743 ** TH Syntax:
@@ -867,11 +867,11 @@
867 ** TH Syntax:
868 **
869 ** unset VAR
870 */
871 static int unset_command(
872 Th_Interp *interp,
873 void *ctx,
874 int argc,
875 const char **argv,
876 int *argl
877 ){
@@ -880,11 +880,11 @@
880 }
881 return Th_UnsetVar(interp, argv[1], argl[1]);
882 }
883
884 int Th_CallSubCommand(
885 Th_Interp *interp,
886 void *ctx,
887 int argc,
888 const char **argv,
889 int *argl,
890 Th_SubCommand *aSub
@@ -916,11 +916,11 @@
916 ** string length STRING
917 ** string range STRING FIRST LAST
918 ** string repeat STRING COUNT
919 */
920 static int string_command(
921 Th_Interp *interp,
922 void *ctx,
923 int argc,
924 const char **argv,
925 int *argl
926 ){
@@ -944,11 +944,11 @@
944 ** TH Syntax:
945 **
946 ** info exists VARNAME
947 */
948 static int info_command(
949 Th_Interp *interp,
950 void *ctx,
951 int argc,
952 const char **argv,
953 int *argl
954 ){
@@ -958,20 +958,20 @@
958 };
959 return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub);
960 }
961
962 /*
963 ** Convert the script level frame specification (used by the commands
964 ** [uplevel] and [upvar]) in (zFrame, nFrame) to an integer frame as
965 ** used by Th_LinkVar() and Th_Eval(). If successful, write the integer
966 ** frame level to *piFrame and return TH_OK. Otherwise, return TH_ERROR
967 ** and leave an error message in the interpreter result.
968 */
969 static int thToFrame(
970 Th_Interp *interp,
971 const char *zFrame,
972 int nFrame,
973 int *piFrame
974 ){
975 int iFrame;
976 if( th_isdigit(zFrame[0]) ){
977 int rc = Th_ToInt(interp, zFrame, nFrame, &iFrame);
@@ -992,11 +992,11 @@
992 ** TH Syntax:
993 **
994 ** uplevel ?LEVEL? SCRIPT
995 */
996 static int uplevel_command(
997 Th_Interp *interp,
998 void *ctx,
999 int argc,
1000 const char **argv,
1001 int *argl
1002 ){
@@ -1010,19 +1010,19 @@
1010 }
1011 return Th_Eval(interp, iFrame, argv[argc-1], -1);
1012 }
1013
1014 /*
1015 ** TH Syntax:
1016 **
1017 ** upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR ...?
1018 */
1019 static int upvar_command(
1020 Th_Interp *interp,
1021 void *ctx,
1022 int argc,
1023 const char **argv,
1024 int *argl
1025 ){
1026 int iVar = 1;
1027 int iFrame = -1;
1028 int rc = TH_OK;
@@ -1030,32 +1030,32 @@
1030
1031 if( TH_OK==thToFrame(0, argv[1], argl[1], &iFrame) ){
1032 iVar++;
1033 }
1034 if( argc==iVar || (argc-iVar)%2 ){
1035 return Th_WrongNumArgs(interp,
1036 "upvar frame othervar myvar ?othervar myvar...?");
1037 }
1038 for(i=iVar; rc==TH_OK && i<argc; i=i+2){
1039 rc = Th_LinkVar(interp, argv[i+1], argl[i+1], iFrame, argv[i], argl[i]);
1040 }
1041 return rc;
1042 }
1043
1044 /*
1045 ** TH Syntax:
1046 **
1047 ** breakpoint ARGS
1048 **
1049 ** This command does nothing at all. Its purpose in life is to serve
1050 ** as a point for setting breakpoints in a debugger.
1051 */
1052 static int breakpoint_command(
1053 Th_Interp *interp,
1054 void *ctx,
1055 int argc,
1056 const char **argv,
1057 int *argl
1058 ){
1059 int cnt = 0;
1060 cnt++;
1061 return TH_OK;
@@ -1078,11 +1078,11 @@
1078 {"if", if_command, 0},
1079 {"info", info_command, 0},
1080 {"lindex", lindex_command, 0},
1081 {"list", list_command, 0},
1082 {"llength", llength_command, 0},
1083 {"proc", proc_command, 0},
1084 {"rename", rename_command, 0},
1085 {"set", set_command, 0},
1086 {"string", string_command, 0},
1087 {"unset", unset_command, 0},
1088 {"uplevel", uplevel_command, 0},
@@ -1089,13 +1089,13 @@
1089 {"upvar", upvar_command, 0},
1090
1091 {"breakpoint", breakpoint_command, 0},
1092
1093 {"return", return_command, 0},
1094 {"break", simple_command, (void *)TH_BREAK},
1095 {"continue", simple_command, (void *)TH_CONTINUE},
1096 {"error", simple_command, (void *)TH_ERROR},
1097
1098 {0, 0, 0}
1099 };
1100 int i;
1101
1102
+10 -2
--- src/th_main.c
+++ src/th_main.c
@@ -57,10 +57,17 @@
5757
nOutstandingMalloc--;
5858
}
5959
free(p);
6060
}
6161
static Th_Vtab vtab = { xMalloc, xFree };
62
+
63
+/*
64
+** Returns the number of outstanding TH1 memory allocations.
65
+*/
66
+int Th_GetOutstandingMalloc(){
67
+ return nOutstandingMalloc;
68
+}
6269
6370
/*
6471
** Generate a TH1 trace message if debugging is enabled.
6572
*/
6673
void Th_Trace(const char *zFormat, ...){
@@ -255,11 +262,11 @@
255262
const char **argv,
256263
int *argl
257264
){
258265
char *zOut;
259266
if( argc>=2 && argl[1]==6 && memcmp(argv[1],"-local",6)==0 ){
260
- zOut = db_text("??", "SELECT datetime('now','localtime')");
267
+ zOut = db_text("??", "SELECT datetime('now'%s)", timeline_utc());
261268
}else{
262269
zOut = db_text("??", "SELECT datetime('now')");
263270
}
264271
Th_SetResult(interp, zOut, -1);
265272
free(zOut);
@@ -1004,11 +1011,12 @@
10041011
}
10051012
if( forceReset || created ){
10061013
th_register_language(g.interp); /* Basic scripting commands. */
10071014
}
10081015
#ifdef FOSSIL_ENABLE_TCL
1009
- if( forceTcl || getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
1016
+ if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 ||
1017
+ db_get_boolean("tcl", 0) ){
10101018
if( !g.tcl.setup ){
10111019
g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
10121020
}
10131021
th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
10141022
}
10151023
--- src/th_main.c
+++ src/th_main.c
@@ -57,10 +57,17 @@
57 nOutstandingMalloc--;
58 }
59 free(p);
60 }
61 static Th_Vtab vtab = { xMalloc, xFree };
 
 
 
 
 
 
 
62
63 /*
64 ** Generate a TH1 trace message if debugging is enabled.
65 */
66 void Th_Trace(const char *zFormat, ...){
@@ -255,11 +262,11 @@
255 const char **argv,
256 int *argl
257 ){
258 char *zOut;
259 if( argc>=2 && argl[1]==6 && memcmp(argv[1],"-local",6)==0 ){
260 zOut = db_text("??", "SELECT datetime('now','localtime')");
261 }else{
262 zOut = db_text("??", "SELECT datetime('now')");
263 }
264 Th_SetResult(interp, zOut, -1);
265 free(zOut);
@@ -1004,11 +1011,12 @@
1004 }
1005 if( forceReset || created ){
1006 th_register_language(g.interp); /* Basic scripting commands. */
1007 }
1008 #ifdef FOSSIL_ENABLE_TCL
1009 if( forceTcl || getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
 
1010 if( !g.tcl.setup ){
1011 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
1012 }
1013 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
1014 }
1015
--- src/th_main.c
+++ src/th_main.c
@@ -57,10 +57,17 @@
57 nOutstandingMalloc--;
58 }
59 free(p);
60 }
61 static Th_Vtab vtab = { xMalloc, xFree };
62
63 /*
64 ** Returns the number of outstanding TH1 memory allocations.
65 */
66 int Th_GetOutstandingMalloc(){
67 return nOutstandingMalloc;
68 }
69
70 /*
71 ** Generate a TH1 trace message if debugging is enabled.
72 */
73 void Th_Trace(const char *zFormat, ...){
@@ -255,11 +262,11 @@
262 const char **argv,
263 int *argl
264 ){
265 char *zOut;
266 if( argc>=2 && argl[1]==6 && memcmp(argv[1],"-local",6)==0 ){
267 zOut = db_text("??", "SELECT datetime('now'%s)", timeline_utc());
268 }else{
269 zOut = db_text("??", "SELECT datetime('now')");
270 }
271 Th_SetResult(interp, zOut, -1);
272 free(zOut);
@@ -1004,11 +1011,12 @@
1011 }
1012 if( forceReset || created ){
1013 th_register_language(g.interp); /* Basic scripting commands. */
1014 }
1015 #ifdef FOSSIL_ENABLE_TCL
1016 if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 ||
1017 db_get_boolean("tcl", 0) ){
1018 if( !g.tcl.setup ){
1019 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
1020 }
1021 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
1022 }
1023
+22 -17
--- src/timeline.c
+++ src/timeline.c
@@ -289,10 +289,11 @@
289289
const char *zBr = 0; /* Branch */
290290
int commentColumn = 3; /* Column containing comment text */
291291
int modPending; /* Pending moderation */
292292
char zTime[20];
293293
294
+ if( zDate==0 ) zDate = "YYYY-MM-DD HH:MM:SS"; /* Something wrong with the repo */
294295
modPending = moderation_pending(rid);
295296
if( tagid ){
296297
if( modPending ) tagid = -tagid;
297298
if( tagid==prevTagid ){
298299
if( tmFlags & TIMELINE_BRIEF ){
@@ -320,11 +321,11 @@
320321
prevWasDivider = 1;
321322
continue;
322323
}
323324
prevWasDivider = 0;
324325
if( dateFormat<2 ){
325
- if( memcmp(zDate, zPrevDate, 10) ){
326
+ if( fossil_strnicmp(zDate, zPrevDate, 10) ){
326327
sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
327328
@ <tr><td>
328329
@ <div class="divider timelineDate">%s(zPrevDate)</div>
329330
@ </td><td></td><td></td></tr>
330331
}
@@ -904,15 +905,16 @@
904905
/*
905906
** Return a pointer to a constant string that forms the basis
906907
** for a timeline query for the WWW interface.
907908
*/
908909
const char *timeline_query_for_www(void){
910
+ static const char *zBase = 0;
909911
static const char zBaseSql[] =
910912
@ SELECT
911913
@ blob.rid AS blobRid,
912914
@ uuid AS uuid,
913
- @ datetime(event.mtime,'localtime') AS timestamp,
915
+ @ datetime(event.mtime%s) AS timestamp,
914916
@ coalesce(ecomment, comment) AS comment,
915917
@ coalesce(euser, user) AS user,
916918
@ blob.rid IN leaf AS leaf,
917919
@ bgcolor AS bgColor,
918920
@ event.type AS eventType,
@@ -923,11 +925,14 @@
923925
@ brief AS brief,
924926
@ event.mtime AS mtime
925927
@ FROM event CROSS JOIN blob
926928
@ WHERE blob.rid=event.objid
927929
;
928
- return zBaseSql;
930
+ if( zBase==0 ){
931
+ zBase = mprintf(zBaseSql, timeline_utc());
932
+ }
933
+ return zBase;
929934
}
930935
931936
/*
932937
** Generate a submenu element with a single parameter change.
933938
*/
@@ -1593,11 +1598,11 @@
15931598
fossil_print("--- entry limit (%d) reached ---\n", nAbsLimit);
15941599
break; /* entry count limit hit, stop. */
15951600
}
15961601
}
15971602
sqlite3_snprintf(sizeof(zUuid), zUuid, "%.10s", zId);
1598
- if( memcmp(zDate, zPrevDate, 10) ){
1603
+ if( fossil_strnicmp(zDate, zPrevDate, 10) ){
15991604
fossil_print("=== %.10s ===\n", zDate);
16001605
memcpy(zPrevDate, zDate, 10);
16011606
nLine++; /* record another line */
16021607
}
16031608
if( zCom==0 ) zCom = "";
@@ -1671,15 +1676,16 @@
16711676
/*
16721677
** Return a pointer to a static string that forms the basis for
16731678
** a timeline query for display on a TTY.
16741679
*/
16751680
const char *timeline_query_for_tty(void){
1681
+ static const char *zBase = 0;
16761682
static const char zBaseSql[] =
16771683
@ SELECT
16781684
@ blob.rid AS rid,
16791685
@ uuid,
1680
- @ datetime(event.mtime,'localtime') AS mDateTime,
1686
+ @ datetime(event.mtime%s) AS mDateTime,
16811687
@ coalesce(ecomment,comment)
16821688
@ || ' (user: ' || coalesce(euser,user,'?')
16831689
@ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
16841690
@ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
16851691
@ FROM tag, tagxref
@@ -1696,11 +1702,14 @@
16961702
@ AND tagxref.tagtype>0
16971703
@ AND tagxref.rid=blob.rid
16981704
@ WHERE blob.rid=event.objid
16991705
@ AND tag.tagname='branch'
17001706
;
1701
- return zBaseSql;
1707
+ if( zBase==0 ){
1708
+ zBase = mprintf(zBaseSql, timeline_utc());
1709
+ }
1710
+ return zBase;
17021711
}
17031712
17041713
/*
17051714
** Return true if the input string is a date in the ISO 8601 format:
17061715
** YYYY-MM-DD.
@@ -1876,32 +1885,28 @@
18761885
print_timeline(&q, n, width, verboseFlag);
18771886
db_finalize(&q);
18781887
}
18791888
18801889
/*
1881
-** This is a version of the "localtime()" function from the standard
1882
-** C library. It converts a unix timestamp (seconds since 1970) into
1883
-** a broken-out local time structure.
1890
+** Return one of two things:
1891
+**
1892
+** ",'localtime'" if the timeline-utc property is set to 0.
18841893
**
1885
-** This modified version of localtime() works like the library localtime()
1886
-** by default. Except if the timeline-utc property is set, this routine
1887
-** uses gmttime() instead. Thus by setting the timeline-utc property, we
1888
-** can get all localtimes to be displayed at UTC time.
1894
+** "" (empty string) otherwise.
18891895
*/
1890
-struct tm *fossil_localtime(const time_t *clock){
1896
+const char *timeline_utc(){
18911897
if( g.fTimeFormat==0 ){
18921898
if( db_get_int("timeline-utc", 1) ){
18931899
g.fTimeFormat = 1;
18941900
}else{
18951901
g.fTimeFormat = 2;
18961902
}
18971903
}
1898
- if( clock==0 ) return 0;
18991904
if( g.fTimeFormat==1 ){
1900
- return gmtime(clock);
1905
+ return "";
19011906
}else{
1902
- return localtime(clock);
1907
+ return ",'localtime'";
19031908
}
19041909
}
19051910
19061911
19071912
/*
19081913
--- src/timeline.c
+++ src/timeline.c
@@ -289,10 +289,11 @@
289 const char *zBr = 0; /* Branch */
290 int commentColumn = 3; /* Column containing comment text */
291 int modPending; /* Pending moderation */
292 char zTime[20];
293
 
294 modPending = moderation_pending(rid);
295 if( tagid ){
296 if( modPending ) tagid = -tagid;
297 if( tagid==prevTagid ){
298 if( tmFlags & TIMELINE_BRIEF ){
@@ -320,11 +321,11 @@
320 prevWasDivider = 1;
321 continue;
322 }
323 prevWasDivider = 0;
324 if( dateFormat<2 ){
325 if( memcmp(zDate, zPrevDate, 10) ){
326 sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
327 @ <tr><td>
328 @ <div class="divider timelineDate">%s(zPrevDate)</div>
329 @ </td><td></td><td></td></tr>
330 }
@@ -904,15 +905,16 @@
904 /*
905 ** Return a pointer to a constant string that forms the basis
906 ** for a timeline query for the WWW interface.
907 */
908 const char *timeline_query_for_www(void){
 
909 static const char zBaseSql[] =
910 @ SELECT
911 @ blob.rid AS blobRid,
912 @ uuid AS uuid,
913 @ datetime(event.mtime,'localtime') AS timestamp,
914 @ coalesce(ecomment, comment) AS comment,
915 @ coalesce(euser, user) AS user,
916 @ blob.rid IN leaf AS leaf,
917 @ bgcolor AS bgColor,
918 @ event.type AS eventType,
@@ -923,11 +925,14 @@
923 @ brief AS brief,
924 @ event.mtime AS mtime
925 @ FROM event CROSS JOIN blob
926 @ WHERE blob.rid=event.objid
927 ;
928 return zBaseSql;
 
 
 
929 }
930
931 /*
932 ** Generate a submenu element with a single parameter change.
933 */
@@ -1593,11 +1598,11 @@
1593 fossil_print("--- entry limit (%d) reached ---\n", nAbsLimit);
1594 break; /* entry count limit hit, stop. */
1595 }
1596 }
1597 sqlite3_snprintf(sizeof(zUuid), zUuid, "%.10s", zId);
1598 if( memcmp(zDate, zPrevDate, 10) ){
1599 fossil_print("=== %.10s ===\n", zDate);
1600 memcpy(zPrevDate, zDate, 10);
1601 nLine++; /* record another line */
1602 }
1603 if( zCom==0 ) zCom = "";
@@ -1671,15 +1676,16 @@
1671 /*
1672 ** Return a pointer to a static string that forms the basis for
1673 ** a timeline query for display on a TTY.
1674 */
1675 const char *timeline_query_for_tty(void){
 
1676 static const char zBaseSql[] =
1677 @ SELECT
1678 @ blob.rid AS rid,
1679 @ uuid,
1680 @ datetime(event.mtime,'localtime') AS mDateTime,
1681 @ coalesce(ecomment,comment)
1682 @ || ' (user: ' || coalesce(euser,user,'?')
1683 @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
1684 @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
1685 @ FROM tag, tagxref
@@ -1696,11 +1702,14 @@
1696 @ AND tagxref.tagtype>0
1697 @ AND tagxref.rid=blob.rid
1698 @ WHERE blob.rid=event.objid
1699 @ AND tag.tagname='branch'
1700 ;
1701 return zBaseSql;
 
 
 
1702 }
1703
1704 /*
1705 ** Return true if the input string is a date in the ISO 8601 format:
1706 ** YYYY-MM-DD.
@@ -1876,32 +1885,28 @@
1876 print_timeline(&q, n, width, verboseFlag);
1877 db_finalize(&q);
1878 }
1879
1880 /*
1881 ** This is a version of the "localtime()" function from the standard
1882 ** C library. It converts a unix timestamp (seconds since 1970) into
1883 ** a broken-out local time structure.
1884 **
1885 ** This modified version of localtime() works like the library localtime()
1886 ** by default. Except if the timeline-utc property is set, this routine
1887 ** uses gmttime() instead. Thus by setting the timeline-utc property, we
1888 ** can get all localtimes to be displayed at UTC time.
1889 */
1890 struct tm *fossil_localtime(const time_t *clock){
1891 if( g.fTimeFormat==0 ){
1892 if( db_get_int("timeline-utc", 1) ){
1893 g.fTimeFormat = 1;
1894 }else{
1895 g.fTimeFormat = 2;
1896 }
1897 }
1898 if( clock==0 ) return 0;
1899 if( g.fTimeFormat==1 ){
1900 return gmtime(clock);
1901 }else{
1902 return localtime(clock);
1903 }
1904 }
1905
1906
1907 /*
1908
--- src/timeline.c
+++ src/timeline.c
@@ -289,10 +289,11 @@
289 const char *zBr = 0; /* Branch */
290 int commentColumn = 3; /* Column containing comment text */
291 int modPending; /* Pending moderation */
292 char zTime[20];
293
294 if( zDate==0 ) zDate = "YYYY-MM-DD HH:MM:SS"; /* Something wrong with the repo */
295 modPending = moderation_pending(rid);
296 if( tagid ){
297 if( modPending ) tagid = -tagid;
298 if( tagid==prevTagid ){
299 if( tmFlags & TIMELINE_BRIEF ){
@@ -320,11 +321,11 @@
321 prevWasDivider = 1;
322 continue;
323 }
324 prevWasDivider = 0;
325 if( dateFormat<2 ){
326 if( fossil_strnicmp(zDate, zPrevDate, 10) ){
327 sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
328 @ <tr><td>
329 @ <div class="divider timelineDate">%s(zPrevDate)</div>
330 @ </td><td></td><td></td></tr>
331 }
@@ -904,15 +905,16 @@
905 /*
906 ** Return a pointer to a constant string that forms the basis
907 ** for a timeline query for the WWW interface.
908 */
909 const char *timeline_query_for_www(void){
910 static const char *zBase = 0;
911 static const char zBaseSql[] =
912 @ SELECT
913 @ blob.rid AS blobRid,
914 @ uuid AS uuid,
915 @ datetime(event.mtime%s) AS timestamp,
916 @ coalesce(ecomment, comment) AS comment,
917 @ coalesce(euser, user) AS user,
918 @ blob.rid IN leaf AS leaf,
919 @ bgcolor AS bgColor,
920 @ event.type AS eventType,
@@ -923,11 +925,14 @@
925 @ brief AS brief,
926 @ event.mtime AS mtime
927 @ FROM event CROSS JOIN blob
928 @ WHERE blob.rid=event.objid
929 ;
930 if( zBase==0 ){
931 zBase = mprintf(zBaseSql, timeline_utc());
932 }
933 return zBase;
934 }
935
936 /*
937 ** Generate a submenu element with a single parameter change.
938 */
@@ -1593,11 +1598,11 @@
1598 fossil_print("--- entry limit (%d) reached ---\n", nAbsLimit);
1599 break; /* entry count limit hit, stop. */
1600 }
1601 }
1602 sqlite3_snprintf(sizeof(zUuid), zUuid, "%.10s", zId);
1603 if( fossil_strnicmp(zDate, zPrevDate, 10) ){
1604 fossil_print("=== %.10s ===\n", zDate);
1605 memcpy(zPrevDate, zDate, 10);
1606 nLine++; /* record another line */
1607 }
1608 if( zCom==0 ) zCom = "";
@@ -1671,15 +1676,16 @@
1676 /*
1677 ** Return a pointer to a static string that forms the basis for
1678 ** a timeline query for display on a TTY.
1679 */
1680 const char *timeline_query_for_tty(void){
1681 static const char *zBase = 0;
1682 static const char zBaseSql[] =
1683 @ SELECT
1684 @ blob.rid AS rid,
1685 @ uuid,
1686 @ datetime(event.mtime%s) AS mDateTime,
1687 @ coalesce(ecomment,comment)
1688 @ || ' (user: ' || coalesce(euser,user,'?')
1689 @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
1690 @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
1691 @ FROM tag, tagxref
@@ -1696,11 +1702,14 @@
1702 @ AND tagxref.tagtype>0
1703 @ AND tagxref.rid=blob.rid
1704 @ WHERE blob.rid=event.objid
1705 @ AND tag.tagname='branch'
1706 ;
1707 if( zBase==0 ){
1708 zBase = mprintf(zBaseSql, timeline_utc());
1709 }
1710 return zBase;
1711 }
1712
1713 /*
1714 ** Return true if the input string is a date in the ISO 8601 format:
1715 ** YYYY-MM-DD.
@@ -1876,32 +1885,28 @@
1885 print_timeline(&q, n, width, verboseFlag);
1886 db_finalize(&q);
1887 }
1888
1889 /*
1890 ** Return one of two things:
1891 **
1892 ** ",'localtime'" if the timeline-utc property is set to 0.
1893 **
1894 ** "" (empty string) otherwise.
 
 
 
1895 */
1896 const char *timeline_utc(){
1897 if( g.fTimeFormat==0 ){
1898 if( db_get_int("timeline-utc", 1) ){
1899 g.fTimeFormat = 1;
1900 }else{
1901 g.fTimeFormat = 2;
1902 }
1903 }
 
1904 if( g.fTimeFormat==1 ){
1905 return "";
1906 }else{
1907 return ",'localtime'";
1908 }
1909 }
1910
1911
1912 /*
1913
+15 -10
--- src/tkt.c
+++ src/tkt.c
@@ -137,12 +137,13 @@
137137
const char *zName;
138138
Stmt q;
139139
int i, n, size, j;
140140
141141
zName = PD("name","-none-");
142
- db_prepare(&q, "SELECT datetime(tkt_mtime,'localtime') AS tkt_datetime, *"
143
- " FROM ticket WHERE tkt_uuid GLOB '%q*'", zName);
142
+ db_prepare(&q, "SELECT datetime(tkt_mtime%s) AS tkt_datetime, *"
143
+ " FROM ticket WHERE tkt_uuid GLOB '%q*'",
144
+ timeline_utc(), zName);
144145
if( db_step(&q)==SQLITE_ROW ){
145146
n = db_column_count(&q);
146147
for(i=0; i<n; i++){
147148
const char *zVal = db_column_text(&q, i);
148149
const char *zName = db_column_name(&q, i);
@@ -534,13 +535,17 @@
534535
}else{
535536
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
536537
db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
537538
}
538539
manifest_crosslink_begin();
539
- result = (manifest_crosslink(rid, pTicket, MC_PERMIT_HOOKS)==0);
540
+ result = (manifest_crosslink(rid, pTicket, MC_NONE)==0);
540541
assert( blob_is_reset(pTicket) );
541
- manifest_crosslink_end();
542
+ if( !result ){
543
+ result = manifest_crosslink_end(MC_PERMIT_HOOKS);
544
+ }else{
545
+ manifest_crosslink_end(MC_NONE);
546
+ }
542547
return result;
543548
}
544549
545550
/*
546551
** Subscript command: submit_ticket
@@ -905,21 +910,21 @@
905910
@ No such ticket: %h(zUuid)
906911
style_footer();
907912
return;
908913
}
909914
db_prepare(&q,
910
- "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
915
+ "SELECT datetime(mtime%s), objid, uuid, NULL, NULL, NULL"
911916
" FROM event, blob"
912917
" WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
913918
" AND blob.rid=event.objid"
914919
" UNION "
915
- "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
920
+ "SELECT datetime(mtime%s), attachid, uuid, src, filename, user"
916921
" FROM attachment, blob"
917922
" WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
918923
" AND blob.rid=attachid"
919924
" ORDER BY 1",
920
- tagid, tagid
925
+ timeline_utc(), tagid, timeline_utc(), tagid
921926
);
922927
while( db_step(&q)==SQLITE_ROW ){
923928
Manifest *pTicket;
924929
char zShort[12];
925930
const char *zDate = db_column_text(&q, 0);
@@ -1214,22 +1219,22 @@
12141219
zTktUuid);
12151220
if( tagid==0 ){
12161221
fossil_fatal("no such ticket %h", zTktUuid);
12171222
}
12181223
db_prepare(&q,
1219
- "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
1224
+ "SELECT datetime(mtime%s), objid, uuid, NULL, NULL, NULL"
12201225
" FROM event, blob"
12211226
" WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
12221227
" AND blob.rid=event.objid"
12231228
" UNION "
1224
- "SELECT datetime(mtime,'localtime'), attachid, uuid, src, "
1229
+ "SELECT datetime(mtime%s), attachid, uuid, src, "
12251230
" filename, user"
12261231
" FROM attachment, blob"
12271232
" WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
12281233
" AND blob.rid=attachid"
12291234
" ORDER BY 1 DESC",
1230
- tagid, tagid
1235
+ timeline_utc(), tagid, timeline_utc(), tagid
12311236
);
12321237
while( db_step(&q)==SQLITE_ROW ){
12331238
Manifest *pTicket;
12341239
char zShort[12];
12351240
const char *zDate = db_column_text(&q, 0);
12361241
--- src/tkt.c
+++ src/tkt.c
@@ -137,12 +137,13 @@
137 const char *zName;
138 Stmt q;
139 int i, n, size, j;
140
141 zName = PD("name","-none-");
142 db_prepare(&q, "SELECT datetime(tkt_mtime,'localtime') AS tkt_datetime, *"
143 " FROM ticket WHERE tkt_uuid GLOB '%q*'", zName);
 
144 if( db_step(&q)==SQLITE_ROW ){
145 n = db_column_count(&q);
146 for(i=0; i<n; i++){
147 const char *zVal = db_column_text(&q, i);
148 const char *zName = db_column_name(&q, i);
@@ -534,13 +535,17 @@
534 }else{
535 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
536 db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
537 }
538 manifest_crosslink_begin();
539 result = (manifest_crosslink(rid, pTicket, MC_PERMIT_HOOKS)==0);
540 assert( blob_is_reset(pTicket) );
541 manifest_crosslink_end();
 
 
 
 
542 return result;
543 }
544
545 /*
546 ** Subscript command: submit_ticket
@@ -905,21 +910,21 @@
905 @ No such ticket: %h(zUuid)
906 style_footer();
907 return;
908 }
909 db_prepare(&q,
910 "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
911 " FROM event, blob"
912 " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
913 " AND blob.rid=event.objid"
914 " UNION "
915 "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
916 " FROM attachment, blob"
917 " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
918 " AND blob.rid=attachid"
919 " ORDER BY 1",
920 tagid, tagid
921 );
922 while( db_step(&q)==SQLITE_ROW ){
923 Manifest *pTicket;
924 char zShort[12];
925 const char *zDate = db_column_text(&q, 0);
@@ -1214,22 +1219,22 @@
1214 zTktUuid);
1215 if( tagid==0 ){
1216 fossil_fatal("no such ticket %h", zTktUuid);
1217 }
1218 db_prepare(&q,
1219 "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
1220 " FROM event, blob"
1221 " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
1222 " AND blob.rid=event.objid"
1223 " UNION "
1224 "SELECT datetime(mtime,'localtime'), attachid, uuid, src, "
1225 " filename, user"
1226 " FROM attachment, blob"
1227 " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
1228 " AND blob.rid=attachid"
1229 " ORDER BY 1 DESC",
1230 tagid, tagid
1231 );
1232 while( db_step(&q)==SQLITE_ROW ){
1233 Manifest *pTicket;
1234 char zShort[12];
1235 const char *zDate = db_column_text(&q, 0);
1236
--- src/tkt.c
+++ src/tkt.c
@@ -137,12 +137,13 @@
137 const char *zName;
138 Stmt q;
139 int i, n, size, j;
140
141 zName = PD("name","-none-");
142 db_prepare(&q, "SELECT datetime(tkt_mtime%s) AS tkt_datetime, *"
143 " FROM ticket WHERE tkt_uuid GLOB '%q*'",
144 timeline_utc(), zName);
145 if( db_step(&q)==SQLITE_ROW ){
146 n = db_column_count(&q);
147 for(i=0; i<n; i++){
148 const char *zVal = db_column_text(&q, i);
149 const char *zName = db_column_name(&q, i);
@@ -534,13 +535,17 @@
535 }else{
536 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
537 db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
538 }
539 manifest_crosslink_begin();
540 result = (manifest_crosslink(rid, pTicket, MC_NONE)==0);
541 assert( blob_is_reset(pTicket) );
542 if( !result ){
543 result = manifest_crosslink_end(MC_PERMIT_HOOKS);
544 }else{
545 manifest_crosslink_end(MC_NONE);
546 }
547 return result;
548 }
549
550 /*
551 ** Subscript command: submit_ticket
@@ -905,21 +910,21 @@
910 @ No such ticket: %h(zUuid)
911 style_footer();
912 return;
913 }
914 db_prepare(&q,
915 "SELECT datetime(mtime%s), objid, uuid, NULL, NULL, NULL"
916 " FROM event, blob"
917 " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
918 " AND blob.rid=event.objid"
919 " UNION "
920 "SELECT datetime(mtime%s), attachid, uuid, src, filename, user"
921 " FROM attachment, blob"
922 " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
923 " AND blob.rid=attachid"
924 " ORDER BY 1",
925 timeline_utc(), tagid, timeline_utc(), tagid
926 );
927 while( db_step(&q)==SQLITE_ROW ){
928 Manifest *pTicket;
929 char zShort[12];
930 const char *zDate = db_column_text(&q, 0);
@@ -1214,22 +1219,22 @@
1219 zTktUuid);
1220 if( tagid==0 ){
1221 fossil_fatal("no such ticket %h", zTktUuid);
1222 }
1223 db_prepare(&q,
1224 "SELECT datetime(mtime%s), objid, uuid, NULL, NULL, NULL"
1225 " FROM event, blob"
1226 " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
1227 " AND blob.rid=event.objid"
1228 " UNION "
1229 "SELECT datetime(mtime%s), attachid, uuid, src, "
1230 " filename, user"
1231 " FROM attachment, blob"
1232 " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
1233 " AND blob.rid=attachid"
1234 " ORDER BY 1 DESC",
1235 timeline_utc(), tagid, timeline_utc(), tagid
1236 );
1237 while( db_step(&q)==SQLITE_ROW ){
1238 Manifest *pTicket;
1239 char zShort[12];
1240 const char *zDate = db_column_text(&q, 0);
1241
+1 -1
--- src/update.c
+++ src/update.c
@@ -353,11 +353,11 @@
353353
db_prepare(&mtimeXfer,
354354
"UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
355355
" WHERE id=:idt"
356356
);
357357
assert( g.zLocalRoot!=0 );
358
- assert( strlen(g.zLocalRoot)>1 );
358
+ assert( strlen(g.zLocalRoot)>0 );
359359
assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' );
360360
while( db_step(&q)==SQLITE_ROW ){
361361
const char *zName = db_column_text(&q, 0); /* The filename from root */
362362
int idv = db_column_int(&q, 1); /* VFILE entry for current */
363363
int ridv = db_column_int(&q, 2); /* RecordID for current */
364364
--- src/update.c
+++ src/update.c
@@ -353,11 +353,11 @@
353 db_prepare(&mtimeXfer,
354 "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
355 " WHERE id=:idt"
356 );
357 assert( g.zLocalRoot!=0 );
358 assert( strlen(g.zLocalRoot)>1 );
359 assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' );
360 while( db_step(&q)==SQLITE_ROW ){
361 const char *zName = db_column_text(&q, 0); /* The filename from root */
362 int idv = db_column_int(&q, 1); /* VFILE entry for current */
363 int ridv = db_column_int(&q, 2); /* RecordID for current */
364
--- src/update.c
+++ src/update.c
@@ -353,11 +353,11 @@
353 db_prepare(&mtimeXfer,
354 "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
355 " WHERE id=:idt"
356 );
357 assert( g.zLocalRoot!=0 );
358 assert( strlen(g.zLocalRoot)>0 );
359 assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' );
360 while( db_step(&q)==SQLITE_ROW ){
361 const char *zName = db_column_text(&q, 0); /* The filename from root */
362 int idv = db_column_int(&q, 1); /* VFILE entry for current */
363 int ridv = db_column_int(&q, 2); /* RecordID for current */
364
+3 -3
--- src/user.c
+++ src/user.c
@@ -450,13 +450,13 @@
450450
cgi_redirectf("%s/access_log?y=%d&n=%d", g.zTop, y, n);
451451
return;
452452
}
453453
style_header("Access Log");
454454
blob_zero(&sql);
455
- blob_append(&sql,
456
- "SELECT uname, ipaddr, datetime(mtime, 'localtime'), success"
457
- " FROM accesslog", -1
455
+ blob_appendf(&sql,
456
+ "SELECT uname, ipaddr, datetime(mtime%s), success"
457
+ " FROM accesslog", timeline_utc()
458458
);
459459
if( y==1 ){
460460
blob_append(&sql, " WHERE success", -1);
461461
}else if( y==2 ){
462462
blob_append(&sql, " WHERE NOT success", -1);
463463
--- src/user.c
+++ src/user.c
@@ -450,13 +450,13 @@
450 cgi_redirectf("%s/access_log?y=%d&n=%d", g.zTop, y, n);
451 return;
452 }
453 style_header("Access Log");
454 blob_zero(&sql);
455 blob_append(&sql,
456 "SELECT uname, ipaddr, datetime(mtime, 'localtime'), success"
457 " FROM accesslog", -1
458 );
459 if( y==1 ){
460 blob_append(&sql, " WHERE success", -1);
461 }else if( y==2 ){
462 blob_append(&sql, " WHERE NOT success", -1);
463
--- src/user.c
+++ src/user.c
@@ -450,13 +450,13 @@
450 cgi_redirectf("%s/access_log?y=%d&n=%d", g.zTop, y, n);
451 return;
452 }
453 style_header("Access Log");
454 blob_zero(&sql);
455 blob_appendf(&sql,
456 "SELECT uname, ipaddr, datetime(mtime%s), success"
457 " FROM accesslog", timeline_utc()
458 );
459 if( y==1 ){
460 blob_append(&sql, " WHERE success", -1);
461 }else if( y==2 ){
462 blob_append(&sql, " WHERE NOT success", -1);
463
+11 -13
--- src/utf8.c
+++ src/utf8.c
@@ -54,18 +54,22 @@
5454
** returned pointer when done.
5555
*/
5656
char *fossil_unicode_to_utf8(const void *zUnicode){
5757
#if defined(_WIN32) || defined(__CYGWIN__)
5858
int nByte = WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, 0, 0, 0, 0);
59
- char *zUtf = sqlite3_malloc( nByte );
60
- if( zUtf==0 ){
61
- return 0;
62
- }
59
+ char *zUtf = fossil_malloc( nByte );
6360
WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, zUtf, nByte, 0, 0);
6461
return zUtf;
6562
#else
66
- return fossil_strdup(zUnicode); /* TODO: implement for unix */
63
+ static Stmt q;
64
+ char *zUtf8;
65
+ db_static_prepare(&q, "SELECT :utf8");
66
+ db_bind_text16(&q, ":utf8", zUnicode);
67
+ db_step(&q);
68
+ zUtf8 = fossil_strdup(db_column_text(&q, 0));
69
+ db_reset(&q);
70
+ return zUtf8;
6771
#endif
6872
}
6973
7074
/*
7175
** Translate UTF-8 to unicode for use in system calls. Return a pointer to the
@@ -73,31 +77,25 @@
7377
** used to store the returned pointer when done.
7478
*/
7579
void *fossil_utf8_to_unicode(const char *zUtf8){
7680
#if defined(_WIN32) || defined(__CYGWIN__)
7781
int nByte = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0);
78
- wchar_t *zUnicode = sqlite3_malloc( nByte * 2 );
79
- if( zUnicode==0 ){
80
- return 0;
81
- }
82
+ wchar_t *zUnicode = fossil_malloc( nByte * 2 );
8283
MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nByte);
8384
return zUnicode;
8485
#else
86
+ assert( 0 ); /* Never used in unix */
8587
return fossil_strdup(zUtf8); /* TODO: implement for unix */
8688
#endif
8789
}
8890
8991
/*
9092
** Deallocate any memory that was previously allocated by
9193
** fossil_unicode_to_utf8().
9294
*/
9395
void fossil_unicode_free(void *pOld){
94
-#if defined(_WIN32) || defined(__CYGWIN__)
95
- sqlite3_free(pOld);
96
-#else
9796
fossil_free(pOld);
98
-#endif
9997
}
10098
10199
#if defined(__APPLE__) && !defined(WITHOUT_ICONV)
102100
# include <iconv.h>
103101
#endif
104102
--- src/utf8.c
+++ src/utf8.c
@@ -54,18 +54,22 @@
54 ** returned pointer when done.
55 */
56 char *fossil_unicode_to_utf8(const void *zUnicode){
57 #if defined(_WIN32) || defined(__CYGWIN__)
58 int nByte = WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, 0, 0, 0, 0);
59 char *zUtf = sqlite3_malloc( nByte );
60 if( zUtf==0 ){
61 return 0;
62 }
63 WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, zUtf, nByte, 0, 0);
64 return zUtf;
65 #else
66 return fossil_strdup(zUnicode); /* TODO: implement for unix */
 
 
 
 
 
 
 
67 #endif
68 }
69
70 /*
71 ** Translate UTF-8 to unicode for use in system calls. Return a pointer to the
@@ -73,31 +77,25 @@
73 ** used to store the returned pointer when done.
74 */
75 void *fossil_utf8_to_unicode(const char *zUtf8){
76 #if defined(_WIN32) || defined(__CYGWIN__)
77 int nByte = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0);
78 wchar_t *zUnicode = sqlite3_malloc( nByte * 2 );
79 if( zUnicode==0 ){
80 return 0;
81 }
82 MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nByte);
83 return zUnicode;
84 #else
 
85 return fossil_strdup(zUtf8); /* TODO: implement for unix */
86 #endif
87 }
88
89 /*
90 ** Deallocate any memory that was previously allocated by
91 ** fossil_unicode_to_utf8().
92 */
93 void fossil_unicode_free(void *pOld){
94 #if defined(_WIN32) || defined(__CYGWIN__)
95 sqlite3_free(pOld);
96 #else
97 fossil_free(pOld);
98 #endif
99 }
100
101 #if defined(__APPLE__) && !defined(WITHOUT_ICONV)
102 # include <iconv.h>
103 #endif
104
--- src/utf8.c
+++ src/utf8.c
@@ -54,18 +54,22 @@
54 ** returned pointer when done.
55 */
56 char *fossil_unicode_to_utf8(const void *zUnicode){
57 #if defined(_WIN32) || defined(__CYGWIN__)
58 int nByte = WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, 0, 0, 0, 0);
59 char *zUtf = fossil_malloc( nByte );
 
 
 
60 WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, zUtf, nByte, 0, 0);
61 return zUtf;
62 #else
63 static Stmt q;
64 char *zUtf8;
65 db_static_prepare(&q, "SELECT :utf8");
66 db_bind_text16(&q, ":utf8", zUnicode);
67 db_step(&q);
68 zUtf8 = fossil_strdup(db_column_text(&q, 0));
69 db_reset(&q);
70 return zUtf8;
71 #endif
72 }
73
74 /*
75 ** Translate UTF-8 to unicode for use in system calls. Return a pointer to the
@@ -73,31 +77,25 @@
77 ** used to store the returned pointer when done.
78 */
79 void *fossil_utf8_to_unicode(const char *zUtf8){
80 #if defined(_WIN32) || defined(__CYGWIN__)
81 int nByte = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0);
82 wchar_t *zUnicode = fossil_malloc( nByte * 2 );
 
 
 
83 MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nByte);
84 return zUnicode;
85 #else
86 assert( 0 ); /* Never used in unix */
87 return fossil_strdup(zUtf8); /* TODO: implement for unix */
88 #endif
89 }
90
91 /*
92 ** Deallocate any memory that was previously allocated by
93 ** fossil_unicode_to_utf8().
94 */
95 void fossil_unicode_free(void *pOld){
 
 
 
96 fossil_free(pOld);
 
97 }
98
99 #if defined(__APPLE__) && !defined(WITHOUT_ICONV)
100 # include <iconv.h>
101 #endif
102
--- src/winfile.c
+++ src/winfile.c
@@ -22,10 +22,14 @@
2222
#ifdef _WIN32
2323
/* This code is for win32 only */
2424
#include <sys/stat.h>
2525
#include <windows.h>
2626
#include "winfile.h"
27
+
28
+#ifndef LABEL_SECURITY_INFORMATION
29
+# define LABEL_SECURITY_INFORMATION (0x00000010L)
30
+#endif
2731
2832
/*
2933
** Fill stat buf with information received from stat() or lstat().
3034
** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on.
3135
**
3236
--- src/winfile.c
+++ src/winfile.c
@@ -22,10 +22,14 @@
22 #ifdef _WIN32
23 /* This code is for win32 only */
24 #include <sys/stat.h>
25 #include <windows.h>
26 #include "winfile.h"
 
 
 
 
27
28 /*
29 ** Fill stat buf with information received from stat() or lstat().
30 ** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on.
31 **
32
--- src/winfile.c
+++ src/winfile.c
@@ -22,10 +22,14 @@
22 #ifdef _WIN32
23 /* This code is for win32 only */
24 #include <sys/stat.h>
25 #include <windows.h>
26 #include "winfile.h"
27
28 #ifndef LABEL_SECURITY_INFORMATION
29 # define LABEL_SECURITY_INFORMATION (0x00000010L)
30 #endif
31
32 /*
33 ** Fill stat buf with information received from stat() or lstat().
34 ** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on.
35 **
36
+15 -16
--- src/xfer.c
+++ src/xfer.c
@@ -852,25 +852,25 @@
852852
853853
/*
854854
** Run the specified TH1 script, if any, and returns 1 on error.
855855
*/
856856
int xfer_run_script(const char *zScript, const char *zUuid){
857
- int result;
857
+ int rc;
858858
if( !zScript ) return TH_OK;
859859
Th_FossilInit(TH_INIT_DEFAULT);
860860
if( zUuid ){
861
- result = Th_SetVar(g.interp, "uuid", -1, zUuid, -1);
862
- if( result!=TH_OK ){
861
+ rc = Th_SetVar(g.interp, "uuid", -1, zUuid, -1);
862
+ if( rc!=TH_OK ){
863863
fossil_error(1, "%s", Th_GetResult(g.interp, 0));
864
- return result;
864
+ return rc;
865865
}
866866
}
867
- result = Th_Eval(g.interp, 0, zScript, -1);
868
- if( result!=TH_OK ){
867
+ rc = Th_Eval(g.interp, 0, zScript, -1);
868
+ if( rc!=TH_OK ){
869869
fossil_error(1, "%s", Th_GetResult(g.interp, 0));
870870
}
871
- return result;
871
+ return rc;
872872
}
873873
874874
/*
875875
** Runs the pre-transfer TH1 script, if any, and returns its return code.
876876
** This script may be run multiple times. If the script performs actions
@@ -881,11 +881,10 @@
881881
** # ... code here
882882
** set common_done 1
883883
** }
884884
*/
885885
int xfer_run_common_script(void){
886
- Th_FossilInit(TH_INIT_DEFAULT);
887886
return xfer_run_script(xfer_common_code(), 0);
888887
}
889888
890889
/*
891890
** If this variable is set, disable login checks. Used for debugging
@@ -915,11 +914,11 @@
915914
int isClone = 0;
916915
int nGimme = 0;
917916
int size;
918917
int recvConfig = 0;
919918
char *zNow;
920
- int result;
919
+ int rc;
921920
922921
if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
923922
fossil_redirect_home();
924923
}
925924
g.zLogin = "anonymous";
@@ -945,12 +944,12 @@
945944
db_begin_transaction();
946945
db_multi_exec(
947946
"CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
948947
);
949948
manifest_crosslink_begin();
950
- result = xfer_run_common_script();
951
- if( result==TH_ERROR ){
949
+ rc = xfer_run_common_script();
950
+ if( rc==TH_ERROR ){
952951
cgi_reset_content();
953952
@ error common\sscript\sfailed:\s%F(g.zErrMsg)
954953
nErr++;
955954
}
956955
while( blob_line(xfer.pIn, &xfer.line) ){
@@ -1273,13 +1272,13 @@
12731272
}
12741273
blobarray_reset(xfer.aToken, xfer.nToken);
12751274
blob_reset(&xfer.line);
12761275
}
12771276
if( isPush ){
1278
- if( result==TH_OK ){
1279
- result = xfer_run_script(xfer_push_code(), 0);
1280
- if( result==TH_ERROR ){
1277
+ if( rc==TH_OK ){
1278
+ rc = xfer_run_script(xfer_push_code(), 0);
1279
+ if( rc==TH_ERROR ){
12811280
cgi_reset_content();
12821281
@ error push\sscript\sfailed:\s%F(g.zErrMsg)
12831282
nErr++;
12841283
}
12851284
}
@@ -1302,11 +1301,11 @@
13021301
}
13031302
if( recvConfig ){
13041303
configure_finalize_receive();
13051304
}
13061305
db_multi_exec("DROP TABLE onremote");
1307
- manifest_crosslink_end();
1306
+ manifest_crosslink_end(MC_PERMIT_HOOKS);
13081307
13091308
/* Send the server timestamp last, in case prior processing happened
13101309
** to use up a significant fraction of our time window.
13111310
*/
13121311
zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
@@ -1929,10 +1928,10 @@
19291928
"%s finished with %lld bytes sent, %lld bytes received\n",
19301929
zOpType, nSent, nRcvd);
19311930
transport_close(GLOBAL_URL());
19321931
transport_global_shutdown(GLOBAL_URL());
19331932
db_multi_exec("DROP TABLE onremote");
1934
- manifest_crosslink_end();
1933
+ manifest_crosslink_end(MC_PERMIT_HOOKS);
19351934
content_enable_dephantomize(1);
19361935
db_end_transaction(0);
19371936
return nErr;
19381937
}
19391938
--- src/xfer.c
+++ src/xfer.c
@@ -852,25 +852,25 @@
852
853 /*
854 ** Run the specified TH1 script, if any, and returns 1 on error.
855 */
856 int xfer_run_script(const char *zScript, const char *zUuid){
857 int result;
858 if( !zScript ) return TH_OK;
859 Th_FossilInit(TH_INIT_DEFAULT);
860 if( zUuid ){
861 result = Th_SetVar(g.interp, "uuid", -1, zUuid, -1);
862 if( result!=TH_OK ){
863 fossil_error(1, "%s", Th_GetResult(g.interp, 0));
864 return result;
865 }
866 }
867 result = Th_Eval(g.interp, 0, zScript, -1);
868 if( result!=TH_OK ){
869 fossil_error(1, "%s", Th_GetResult(g.interp, 0));
870 }
871 return result;
872 }
873
874 /*
875 ** Runs the pre-transfer TH1 script, if any, and returns its return code.
876 ** This script may be run multiple times. If the script performs actions
@@ -881,11 +881,10 @@
881 ** # ... code here
882 ** set common_done 1
883 ** }
884 */
885 int xfer_run_common_script(void){
886 Th_FossilInit(TH_INIT_DEFAULT);
887 return xfer_run_script(xfer_common_code(), 0);
888 }
889
890 /*
891 ** If this variable is set, disable login checks. Used for debugging
@@ -915,11 +914,11 @@
915 int isClone = 0;
916 int nGimme = 0;
917 int size;
918 int recvConfig = 0;
919 char *zNow;
920 int result;
921
922 if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
923 fossil_redirect_home();
924 }
925 g.zLogin = "anonymous";
@@ -945,12 +944,12 @@
945 db_begin_transaction();
946 db_multi_exec(
947 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
948 );
949 manifest_crosslink_begin();
950 result = xfer_run_common_script();
951 if( result==TH_ERROR ){
952 cgi_reset_content();
953 @ error common\sscript\sfailed:\s%F(g.zErrMsg)
954 nErr++;
955 }
956 while( blob_line(xfer.pIn, &xfer.line) ){
@@ -1273,13 +1272,13 @@
1273 }
1274 blobarray_reset(xfer.aToken, xfer.nToken);
1275 blob_reset(&xfer.line);
1276 }
1277 if( isPush ){
1278 if( result==TH_OK ){
1279 result = xfer_run_script(xfer_push_code(), 0);
1280 if( result==TH_ERROR ){
1281 cgi_reset_content();
1282 @ error push\sscript\sfailed:\s%F(g.zErrMsg)
1283 nErr++;
1284 }
1285 }
@@ -1302,11 +1301,11 @@
1302 }
1303 if( recvConfig ){
1304 configure_finalize_receive();
1305 }
1306 db_multi_exec("DROP TABLE onremote");
1307 manifest_crosslink_end();
1308
1309 /* Send the server timestamp last, in case prior processing happened
1310 ** to use up a significant fraction of our time window.
1311 */
1312 zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
@@ -1929,10 +1928,10 @@
1929 "%s finished with %lld bytes sent, %lld bytes received\n",
1930 zOpType, nSent, nRcvd);
1931 transport_close(GLOBAL_URL());
1932 transport_global_shutdown(GLOBAL_URL());
1933 db_multi_exec("DROP TABLE onremote");
1934 manifest_crosslink_end();
1935 content_enable_dephantomize(1);
1936 db_end_transaction(0);
1937 return nErr;
1938 }
1939
--- src/xfer.c
+++ src/xfer.c
@@ -852,25 +852,25 @@
852
853 /*
854 ** Run the specified TH1 script, if any, and returns 1 on error.
855 */
856 int xfer_run_script(const char *zScript, const char *zUuid){
857 int rc;
858 if( !zScript ) return TH_OK;
859 Th_FossilInit(TH_INIT_DEFAULT);
860 if( zUuid ){
861 rc = Th_SetVar(g.interp, "uuid", -1, zUuid, -1);
862 if( rc!=TH_OK ){
863 fossil_error(1, "%s", Th_GetResult(g.interp, 0));
864 return rc;
865 }
866 }
867 rc = Th_Eval(g.interp, 0, zScript, -1);
868 if( rc!=TH_OK ){
869 fossil_error(1, "%s", Th_GetResult(g.interp, 0));
870 }
871 return rc;
872 }
873
874 /*
875 ** Runs the pre-transfer TH1 script, if any, and returns its return code.
876 ** This script may be run multiple times. If the script performs actions
@@ -881,11 +881,10 @@
881 ** # ... code here
882 ** set common_done 1
883 ** }
884 */
885 int xfer_run_common_script(void){
 
886 return xfer_run_script(xfer_common_code(), 0);
887 }
888
889 /*
890 ** If this variable is set, disable login checks. Used for debugging
@@ -915,11 +914,11 @@
914 int isClone = 0;
915 int nGimme = 0;
916 int size;
917 int recvConfig = 0;
918 char *zNow;
919 int rc;
920
921 if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
922 fossil_redirect_home();
923 }
924 g.zLogin = "anonymous";
@@ -945,12 +944,12 @@
944 db_begin_transaction();
945 db_multi_exec(
946 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
947 );
948 manifest_crosslink_begin();
949 rc = xfer_run_common_script();
950 if( rc==TH_ERROR ){
951 cgi_reset_content();
952 @ error common\sscript\sfailed:\s%F(g.zErrMsg)
953 nErr++;
954 }
955 while( blob_line(xfer.pIn, &xfer.line) ){
@@ -1273,13 +1272,13 @@
1272 }
1273 blobarray_reset(xfer.aToken, xfer.nToken);
1274 blob_reset(&xfer.line);
1275 }
1276 if( isPush ){
1277 if( rc==TH_OK ){
1278 rc = xfer_run_script(xfer_push_code(), 0);
1279 if( rc==TH_ERROR ){
1280 cgi_reset_content();
1281 @ error push\sscript\sfailed:\s%F(g.zErrMsg)
1282 nErr++;
1283 }
1284 }
@@ -1302,11 +1301,11 @@
1301 }
1302 if( recvConfig ){
1303 configure_finalize_receive();
1304 }
1305 db_multi_exec("DROP TABLE onremote");
1306 manifest_crosslink_end(MC_PERMIT_HOOKS);
1307
1308 /* Send the server timestamp last, in case prior processing happened
1309 ** to use up a significant fraction of our time window.
1310 */
1311 zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
@@ -1929,10 +1928,10 @@
1928 "%s finished with %lld bytes sent, %lld bytes received\n",
1929 zOpType, nSent, nRcvd);
1930 transport_close(GLOBAL_URL());
1931 transport_global_shutdown(GLOBAL_URL());
1932 db_multi_exec("DROP TABLE onremote");
1933 manifest_crosslink_end(MC_PERMIT_HOOKS);
1934 content_enable_dephantomize(1);
1935 db_end_transaction(0);
1936 return nErr;
1937 }
1938
+142
--- test/th1.test
+++ test/th1.test
@@ -65,5 +65,147 @@
6565
6666
###############################################################################
6767
6868
fossil test-th-eval --th-open-config "setting -strict -- --"
6969
test th1-setting-10 {$RESULT eq {TH_ERROR: no value for setting "--"}}
70
+
71
+###############################################################################
72
+
73
+fossil test-th-eval "expr 42/0"
74
+test th1-divide-by-zero-1 {$RESULT eq {TH_ERROR: Divide by 0: 42}}
75
+
76
+###############################################################################
77
+
78
+fossil test-th-eval "expr 42/0.0"
79
+test th1-divide-by-zero-2 {$RESULT eq {TH_ERROR: Divide by 0: 42}}
80
+
81
+###############################################################################
82
+
83
+fossil test-th-eval "expr 42.0/0"
84
+test th1-divide-by-zero-3 {$RESULT eq {TH_ERROR: Divide by 0: 42.0}}
85
+
86
+###############################################################################
87
+
88
+fossil test-th-eval "expr 42.0/0.0"
89
+test th1-divide-by-zero-4 {$RESULT eq {TH_ERROR: Divide by 0: 42.0}}
90
+
91
+###############################################################################
92
+
93
+fossil test-th-eval "expr 42%0"
94
+test th1-modulus-by-zero-1 {$RESULT eq {TH_ERROR: Modulo by 0: 42}}
95
+
96
+###############################################################################
97
+
98
+fossil test-th-eval "expr 42%0.0"
99
+test th1-modulus-by-zero-2 {$RESULT eq {TH_ERROR: expected integer, got: "0.0"}}
100
+
101
+###############################################################################
102
+
103
+fossil test-th-eval "expr 42.0%0"
104
+test th1-modulus-by-zero-3 {$RESULT eq \
105
+{TH_ERROR: expected integer, got: "42.0"}}
106
+
107
+###############################################################################
108
+
109
+fossil test-th-eval "expr 42.0%0.0"
110
+test th1-modulus-by-zero-4 {$RESULT eq \
111
+{TH_ERROR: expected integer, got: "42.0"}}
112
+
113
+###############################################################################
114
+
115
+fossil test-th-eval "set var 1; info exists var"
116
+test th1-info-exists-1 {$RESULT eq {1}}
117
+
118
+###############################################################################
119
+
120
+fossil test-th-eval "set var 1; unset var; info exists var"
121
+test th1-info-exists-2 {$RESULT eq {0}}
122
+
123
+###############################################################################
124
+
125
+fossil test-th-eval "set var 1; unset var; set var 2; info exists var"
126
+test th1-info-exists-3 {$RESULT eq {1}}
127
+
128
+###############################################################################
129
+
130
+fossil test-th-eval "set var 1; expr {\$var+0}"
131
+test th1-info-exists-4 {$RESULT eq {1}}
132
+
133
+###############################################################################
134
+
135
+fossil test-th-eval "set var 1; unset var; expr {\$var+0}"
136
+test th1-info-exists-5 {$RESULT eq {TH_ERROR: no such variable: var}}
137
+
138
+###############################################################################
139
+
140
+fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141
+test th1-info-exists-6 {$RESULT eq {bad}}
142
+
143
+###############################################################################
144
+
145
+fossil test-th-eval "set var(1) 1; info exists var"
146
+test th1-info-exists-7 {$RESULT eq {1}}
147
+
148
+###############################################################################
149
+
150
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151
+test th1-info-exists-8 {$RESULT eq {1}}
152
+
153
+###############################################################################
154
+
155
+fossil test-th-eval "set var(1) 1; unset var; info exists var"
156
+test th1-info-exists-9 {$RESULT eq {0}}
157
+
158
+###############################################################################
159
+
160
+fossil test-th-eval "set var(1) 1; info exists var(1)"
161
+test th1-info-exists-10 {$RESULT eq {1}}
162
+
163
+###############################################################################
164
+
165
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166
+test th1-info-exists-11 {$RESULT eq {0}}
167
+
168
+###############################################################################
169
+
170
+fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171
+test th1-info-exists-12 {$RESULT eq {0}}
172
+
173
+###############################################################################
174
+
175
+fossil test-th-eval "set var 1; unset var"
176
+test th1-unset-1 {$RESULT eq {var}}
177
+
178
+###############################################################################
179
+
180
+fossil test-th-eval "unset var"
181
+test th1-unset-2 {$RESULT eq {TH_ERROR: no such variable: var}}
182
+
183
+###############################################################################
184
+
185
+fossil test-th-eval "set var 1; unset var; unset var"
186
+test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
+
188
+###############################################################################
189
+
190
+fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191
+test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
+
193
+###############################################################################
194
+
195
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196
+test th1-unset-5 {$RESULT eq {1}}
197
+
198
+###############################################################################
199
+
200
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201
+test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
+
203
+###############################################################################
204
+
205
+fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206
+test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
+
208
+###############################################################################
209
+
210
+fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211
+test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
70212
71213
ADDED test/utf16le.txt
--- test/th1.test
+++ test/th1.test
@@ -65,5 +65,147 @@
65
66 ###############################################################################
67
68 fossil test-th-eval --th-open-config "setting -strict -- --"
69 test th1-setting-10 {$RESULT eq {TH_ERROR: no value for setting "--"}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
71 DDED test/utf16le.txt
--- test/th1.test
+++ test/th1.test
@@ -65,5 +65,147 @@
65
66 ###############################################################################
67
68 fossil test-th-eval --th-open-config "setting -strict -- --"
69 test th1-setting-10 {$RESULT eq {TH_ERROR: no value for setting "--"}}
70
71 ###############################################################################
72
73 fossil test-th-eval "expr 42/0"
74 test th1-divide-by-zero-1 {$RESULT eq {TH_ERROR: Divide by 0: 42}}
75
76 ###############################################################################
77
78 fossil test-th-eval "expr 42/0.0"
79 test th1-divide-by-zero-2 {$RESULT eq {TH_ERROR: Divide by 0: 42}}
80
81 ###############################################################################
82
83 fossil test-th-eval "expr 42.0/0"
84 test th1-divide-by-zero-3 {$RESULT eq {TH_ERROR: Divide by 0: 42.0}}
85
86 ###############################################################################
87
88 fossil test-th-eval "expr 42.0/0.0"
89 test th1-divide-by-zero-4 {$RESULT eq {TH_ERROR: Divide by 0: 42.0}}
90
91 ###############################################################################
92
93 fossil test-th-eval "expr 42%0"
94 test th1-modulus-by-zero-1 {$RESULT eq {TH_ERROR: Modulo by 0: 42}}
95
96 ###############################################################################
97
98 fossil test-th-eval "expr 42%0.0"
99 test th1-modulus-by-zero-2 {$RESULT eq {TH_ERROR: expected integer, got: "0.0"}}
100
101 ###############################################################################
102
103 fossil test-th-eval "expr 42.0%0"
104 test th1-modulus-by-zero-3 {$RESULT eq \
105 {TH_ERROR: expected integer, got: "42.0"}}
106
107 ###############################################################################
108
109 fossil test-th-eval "expr 42.0%0.0"
110 test th1-modulus-by-zero-4 {$RESULT eq \
111 {TH_ERROR: expected integer, got: "42.0"}}
112
113 ###############################################################################
114
115 fossil test-th-eval "set var 1; info exists var"
116 test th1-info-exists-1 {$RESULT eq {1}}
117
118 ###############################################################################
119
120 fossil test-th-eval "set var 1; unset var; info exists var"
121 test th1-info-exists-2 {$RESULT eq {0}}
122
123 ###############################################################################
124
125 fossil test-th-eval "set var 1; unset var; set var 2; info exists var"
126 test th1-info-exists-3 {$RESULT eq {1}}
127
128 ###############################################################################
129
130 fossil test-th-eval "set var 1; expr {\$var+0}"
131 test th1-info-exists-4 {$RESULT eq {1}}
132
133 ###############################################################################
134
135 fossil test-th-eval "set var 1; unset var; expr {\$var+0}"
136 test th1-info-exists-5 {$RESULT eq {TH_ERROR: no such variable: var}}
137
138 ###############################################################################
139
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
145 fossil test-th-eval "set var(1) 1; info exists var"
146 test th1-info-exists-7 {$RESULT eq {1}}
147
148 ###############################################################################
149
150 fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151 test th1-info-exists-8 {$RESULT eq {1}}
152
153 ###############################################################################
154
155 fossil test-th-eval "set var(1) 1; unset var; info exists var"
156 test th1-info-exists-9 {$RESULT eq {0}}
157
158 ###############################################################################
159
160 fossil test-th-eval "set var(1) 1; info exists var(1)"
161 test th1-info-exists-10 {$RESULT eq {1}}
162
163 ###############################################################################
164
165 fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166 test th1-info-exists-11 {$RESULT eq {0}}
167
168 ###############################################################################
169
170 fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171 test th1-info-exists-12 {$RESULT eq {0}}
172
173 ###############################################################################
174
175 fossil test-th-eval "set var 1; unset var"
176 test th1-unset-1 {$RESULT eq {var}}
177
178 ###############################################################################
179
180 fossil test-th-eval "unset var"
181 test th1-unset-2 {$RESULT eq {TH_ERROR: no such variable: var}}
182
183 ###############################################################################
184
185 fossil test-th-eval "set var 1; unset var; unset var"
186 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
188 ###############################################################################
189
190 fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191 test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
193 ###############################################################################
194
195 fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196 test th1-unset-5 {$RESULT eq {1}}
197
198 ###############################################################################
199
200 fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201 test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
203 ###############################################################################
204
205 fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206 test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
208 ###############################################################################
209
210 fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211 test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
213 DDED test/utf16le.txt

Binary file

--- test/valgrind-www.tcl
+++ test/valgrind-www.tcl
@@ -12,11 +12,13 @@
1212
#
1313
proc run_query {url} {
1414
set fd [open q.txt w]
1515
puts $fd "GET $url HTTP/1.0\r\n\r"
1616
close $fd
17
- return [exec valgrind ./fossil test-http <q.txt 2>@ stderr]
17
+ set msg {}
18
+ catch {exec valgrind ./fossil test-http <q.txt 2>@ stderr} msg
19
+ return $msg
1820
}
1921
set todo {}
2022
foreach url {
2123
/home
2224
/timeline
2325
--- test/valgrind-www.tcl
+++ test/valgrind-www.tcl
@@ -12,11 +12,13 @@
12 #
13 proc run_query {url} {
14 set fd [open q.txt w]
15 puts $fd "GET $url HTTP/1.0\r\n\r"
16 close $fd
17 return [exec valgrind ./fossil test-http <q.txt 2>@ stderr]
 
 
18 }
19 set todo {}
20 foreach url {
21 /home
22 /timeline
23
--- test/valgrind-www.tcl
+++ test/valgrind-www.tcl
@@ -12,11 +12,13 @@
12 #
13 proc run_query {url} {
14 set fd [open q.txt w]
15 puts $fd "GET $url HTTP/1.0\r\n\r"
16 close $fd
17 set msg {}
18 catch {exec valgrind ./fossil test-http <q.txt 2>@ stderr} msg
19 return $msg
20 }
21 set todo {}
22 foreach url {
23 /home
24 /timeline
25
--- win/Makefile.PellesCGMake
+++ win/Makefile.PellesCGMake
@@ -83,17 +83,17 @@
8383
8484
# define the sqlite files, which need special flags on compile
8585
SQLITESRC=sqlite3.c
8686
ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
8787
SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
88
-SQLITEDEFINES=-Dlocaltime=fossil_localtime -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS
88
+SQLITEDEFINES=-DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS
8989
9090
# define the sqlite shell files, which need special flags on compile
9191
SQLITESHELLSRC=shell.c
9292
ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf))
9393
SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj))
94
-SQLITESHELLDEFINES=-Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -Dsqlite3_strglob=strglob -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
94
+SQLITESHELLDEFINES=-Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
9595
9696
# define the th scripting files, which need special flags on compile
9797
THSRC=th.c th_lang.c
9898
ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf))
9999
THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj))
100100
--- win/Makefile.PellesCGMake
+++ win/Makefile.PellesCGMake
@@ -83,17 +83,17 @@
83
84 # define the sqlite files, which need special flags on compile
85 SQLITESRC=sqlite3.c
86 ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
87 SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
88 SQLITEDEFINES=-Dlocaltime=fossil_localtime -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS
89
90 # define the sqlite shell files, which need special flags on compile
91 SQLITESHELLSRC=shell.c
92 ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf))
93 SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj))
94 SQLITESHELLDEFINES=-Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -Dsqlite3_strglob=strglob -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
95
96 # define the th scripting files, which need special flags on compile
97 THSRC=th.c th_lang.c
98 ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf))
99 THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj))
100
--- win/Makefile.PellesCGMake
+++ win/Makefile.PellesCGMake
@@ -83,17 +83,17 @@
83
84 # define the sqlite files, which need special flags on compile
85 SQLITESRC=sqlite3.c
86 ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
87 SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
88 SQLITEDEFINES=-DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS
89
90 # define the sqlite shell files, which need special flags on compile
91 SQLITESHELLSRC=shell.c
92 ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf))
93 SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj))
94 SQLITESHELLDEFINES=-Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
95
96 # define the th scripting files, which need special flags on compile
97 THSRC=th.c th_lang.c
98 ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf))
99 THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj))
100
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -24,13 +24,13 @@
2424
CFLAGS = -o
2525
BCC = $(DMDIR)\bin\dmc $(CFLAGS)
2626
TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
2727
LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32
2828
29
-SQLITE_OPTIONS = -Dlocaltime=fossil_localtime -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS
29
+SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS
3030
31
-SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -Dsqlite3_strglob=strglob -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
31
+SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
3232
3333
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_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c 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 util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
3434
3535
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_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\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)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
3636
3737
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -24,13 +24,13 @@
24 CFLAGS = -o
25 BCC = $(DMDIR)\bin\dmc $(CFLAGS)
26 TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
27 LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32
28
29 SQLITE_OPTIONS = -Dlocaltime=fossil_localtime -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS
30
31 SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -Dsqlite3_strglob=strglob -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
32
33 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_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c 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 util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
34
35 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_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\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)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
36
37
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -24,13 +24,13 @@
24 CFLAGS = -o
25 BCC = $(DMDIR)\bin\dmc $(CFLAGS)
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_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS
30
31 SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
32
33 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_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c 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 util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
34
35 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_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\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)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
36
37
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -84,12 +84,12 @@
8484
#### The directories where the OpenSSL include and library files are located.
8585
# The recommended usage here is to use the Sysinternals junction tool
8686
# to create a hard link between an "openssl-1.x" sub-directory of the
8787
# Fossil source code directory and the target OpenSSL source directory.
8888
#
89
-OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1e/include
90
-OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1e
89
+OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1f/include
90
+OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1f
9191
9292
#### Either the directory where the Tcl library is installed or the Tcl
9393
# source code directory resides (depending on the value of the macro
9494
# FOSSIL_TCL_SOURCE). If this points to the Tcl install directory,
9595
# this directory must have "include" and "lib" sub-directories. If
@@ -1698,12 +1698,11 @@
16981698
$(OBJDIR)/zip.o: $(OBJDIR)/zip_.c $(OBJDIR)/zip.h $(SRCDIR)/config.h
16991699
$(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c
17001700
17011701
$(OBJDIR)/zip.h: $(OBJDIR)/headers
17021702
1703
-SQLITE_OPTIONS = -Dlocaltime=fossil_localtime \
1704
- -DSQLITE_OMIT_LOAD_EXTENSION=1 \
1703
+SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 \
17051704
-DSQLITE_ENABLE_LOCKING_STYLE=0 \
17061705
-DSQLITE_THREADSAFE=0 \
17071706
-DSQLITE_DEFAULT_FILE_FORMAT=4 \
17081707
-DSQLITE_OMIT_DEPRECATED \
17091708
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
@@ -1711,23 +1710,22 @@
17111710
-DSQLITE_USE_MALLOC_H \
17121711
-DSQLITE_USE_MSIZE
17131712
17141713
SHELL_OPTIONS = -Dmain=sqlite3_shell \
17151714
-DSQLITE_OMIT_LOAD_EXTENSION=1 \
1716
- -Dsqlite3_strglob=strglob \
17171715
-Dgetenv=fossil_getenv \
17181716
-Dfopen=fossil_fopen
17191717
1720
-$(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c
1718
+$(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c win/Makefile.mingw
17211719
$(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o
17221720
17231721
$(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
17241722
$(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o
17251723
17261724
$(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_status.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
17271725
1728
-$(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h
1726
+$(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h win/Makefile.mingw
17291727
$(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o
17301728
17311729
$(OBJDIR)/th.o: $(SRCDIR)/th.c
17321730
$(XTCC) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
17331731
17341732
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -84,12 +84,12 @@
84 #### The directories where the OpenSSL include and library files are located.
85 # The recommended usage here is to use the Sysinternals junction tool
86 # to create a hard link between an "openssl-1.x" sub-directory of the
87 # Fossil source code directory and the target OpenSSL source directory.
88 #
89 OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1e/include
90 OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1e
91
92 #### Either the directory where the Tcl library is installed or the Tcl
93 # source code directory resides (depending on the value of the macro
94 # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory,
95 # this directory must have "include" and "lib" sub-directories. If
@@ -1698,12 +1698,11 @@
1698 $(OBJDIR)/zip.o: $(OBJDIR)/zip_.c $(OBJDIR)/zip.h $(SRCDIR)/config.h
1699 $(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c
1700
1701 $(OBJDIR)/zip.h: $(OBJDIR)/headers
1702
1703 SQLITE_OPTIONS = -Dlocaltime=fossil_localtime \
1704 -DSQLITE_OMIT_LOAD_EXTENSION=1 \
1705 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
1706 -DSQLITE_THREADSAFE=0 \
1707 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
1708 -DSQLITE_OMIT_DEPRECATED \
1709 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
@@ -1711,23 +1710,22 @@
1711 -DSQLITE_USE_MALLOC_H \
1712 -DSQLITE_USE_MSIZE
1713
1714 SHELL_OPTIONS = -Dmain=sqlite3_shell \
1715 -DSQLITE_OMIT_LOAD_EXTENSION=1 \
1716 -Dsqlite3_strglob=strglob \
1717 -Dgetenv=fossil_getenv \
1718 -Dfopen=fossil_fopen
1719
1720 $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c
1721 $(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o
1722
1723 $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
1724 $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o
1725
1726 $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_status.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
1727
1728 $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h
1729 $(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o
1730
1731 $(OBJDIR)/th.o: $(SRCDIR)/th.c
1732 $(XTCC) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
1733
1734
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -84,12 +84,12 @@
84 #### The directories where the OpenSSL include and library files are located.
85 # The recommended usage here is to use the Sysinternals junction tool
86 # to create a hard link between an "openssl-1.x" sub-directory of the
87 # Fossil source code directory and the target OpenSSL source directory.
88 #
89 OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1f/include
90 OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1f
91
92 #### Either the directory where the Tcl library is installed or the Tcl
93 # source code directory resides (depending on the value of the macro
94 # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory,
95 # this directory must have "include" and "lib" sub-directories. If
@@ -1698,12 +1698,11 @@
1698 $(OBJDIR)/zip.o: $(OBJDIR)/zip_.c $(OBJDIR)/zip.h $(SRCDIR)/config.h
1699 $(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c
1700
1701 $(OBJDIR)/zip.h: $(OBJDIR)/headers
1702
1703 SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 \
 
1704 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
1705 -DSQLITE_THREADSAFE=0 \
1706 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
1707 -DSQLITE_OMIT_DEPRECATED \
1708 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
@@ -1711,23 +1710,22 @@
1710 -DSQLITE_USE_MALLOC_H \
1711 -DSQLITE_USE_MSIZE
1712
1713 SHELL_OPTIONS = -Dmain=sqlite3_shell \
1714 -DSQLITE_OMIT_LOAD_EXTENSION=1 \
 
1715 -Dgetenv=fossil_getenv \
1716 -Dfopen=fossil_fopen
1717
1718 $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c win/Makefile.mingw
1719 $(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o
1720
1721 $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
1722 $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o
1723
1724 $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_status.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
1725
1726 $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h win/Makefile.mingw
1727 $(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o
1728
1729 $(OBJDIR)/th.o: $(SRCDIR)/th.c
1730 $(XTCC) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
1731
1732
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -84,12 +84,12 @@
8484
#### The directories where the OpenSSL include and library files are located.
8585
# The recommended usage here is to use the Sysinternals junction tool
8686
# to create a hard link between an "openssl-1.x" sub-directory of the
8787
# Fossil source code directory and the target OpenSSL source directory.
8888
#
89
-OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1e/include
90
-OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1e
89
+OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1f/include
90
+OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1f
9191
9292
#### Either the directory where the Tcl library is installed or the Tcl
9393
# source code directory resides (depending on the value of the macro
9494
# FOSSIL_TCL_SOURCE). If this points to the Tcl install directory,
9595
# this directory must have "include" and "lib" sub-directories. If
@@ -1698,12 +1698,11 @@
16981698
$(OBJDIR)/zip.o: $(OBJDIR)/zip_.c $(OBJDIR)/zip.h $(SRCDIR)/config.h
16991699
$(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c
17001700
17011701
$(OBJDIR)/zip.h: $(OBJDIR)/headers
17021702
1703
-SQLITE_OPTIONS = -Dlocaltime=fossil_localtime \
1704
- -DSQLITE_OMIT_LOAD_EXTENSION=1 \
1703
+SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 \
17051704
-DSQLITE_ENABLE_LOCKING_STYLE=0 \
17061705
-DSQLITE_THREADSAFE=0 \
17071706
-DSQLITE_DEFAULT_FILE_FORMAT=4 \
17081707
-DSQLITE_OMIT_DEPRECATED \
17091708
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
@@ -1711,23 +1710,22 @@
17111710
-DSQLITE_USE_MALLOC_H \
17121711
-DSQLITE_USE_MSIZE
17131712
17141713
SHELL_OPTIONS = -Dmain=sqlite3_shell \
17151714
-DSQLITE_OMIT_LOAD_EXTENSION=1 \
1716
- -Dsqlite3_strglob=strglob \
17171715
-Dgetenv=fossil_getenv \
17181716
-Dfopen=fossil_fopen
17191717
1720
-$(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c
1718
+$(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c win/Makefile.mingw.mistachkin
17211719
$(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o
17221720
17231721
$(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
17241722
$(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o
17251723
17261724
$(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_status.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
17271725
1728
-$(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h
1726
+$(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h win/Makefile.mingw.mistachkin
17291727
$(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o
17301728
17311729
$(OBJDIR)/th.o: $(SRCDIR)/th.c
17321730
$(XTCC) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
17331731
17341732
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -84,12 +84,12 @@
84 #### The directories where the OpenSSL include and library files are located.
85 # The recommended usage here is to use the Sysinternals junction tool
86 # to create a hard link between an "openssl-1.x" sub-directory of the
87 # Fossil source code directory and the target OpenSSL source directory.
88 #
89 OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1e/include
90 OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1e
91
92 #### Either the directory where the Tcl library is installed or the Tcl
93 # source code directory resides (depending on the value of the macro
94 # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory,
95 # this directory must have "include" and "lib" sub-directories. If
@@ -1698,12 +1698,11 @@
1698 $(OBJDIR)/zip.o: $(OBJDIR)/zip_.c $(OBJDIR)/zip.h $(SRCDIR)/config.h
1699 $(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c
1700
1701 $(OBJDIR)/zip.h: $(OBJDIR)/headers
1702
1703 SQLITE_OPTIONS = -Dlocaltime=fossil_localtime \
1704 -DSQLITE_OMIT_LOAD_EXTENSION=1 \
1705 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
1706 -DSQLITE_THREADSAFE=0 \
1707 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
1708 -DSQLITE_OMIT_DEPRECATED \
1709 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
@@ -1711,23 +1710,22 @@
1711 -DSQLITE_USE_MALLOC_H \
1712 -DSQLITE_USE_MSIZE
1713
1714 SHELL_OPTIONS = -Dmain=sqlite3_shell \
1715 -DSQLITE_OMIT_LOAD_EXTENSION=1 \
1716 -Dsqlite3_strglob=strglob \
1717 -Dgetenv=fossil_getenv \
1718 -Dfopen=fossil_fopen
1719
1720 $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c
1721 $(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o
1722
1723 $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
1724 $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o
1725
1726 $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_status.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
1727
1728 $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h
1729 $(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o
1730
1731 $(OBJDIR)/th.o: $(SRCDIR)/th.c
1732 $(XTCC) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
1733
1734
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -84,12 +84,12 @@
84 #### The directories where the OpenSSL include and library files are located.
85 # The recommended usage here is to use the Sysinternals junction tool
86 # to create a hard link between an "openssl-1.x" sub-directory of the
87 # Fossil source code directory and the target OpenSSL source directory.
88 #
89 OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1f/include
90 OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1f
91
92 #### Either the directory where the Tcl library is installed or the Tcl
93 # source code directory resides (depending on the value of the macro
94 # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory,
95 # this directory must have "include" and "lib" sub-directories. If
@@ -1698,12 +1698,11 @@
1698 $(OBJDIR)/zip.o: $(OBJDIR)/zip_.c $(OBJDIR)/zip.h $(SRCDIR)/config.h
1699 $(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c
1700
1701 $(OBJDIR)/zip.h: $(OBJDIR)/headers
1702
1703 SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 \
 
1704 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
1705 -DSQLITE_THREADSAFE=0 \
1706 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
1707 -DSQLITE_OMIT_DEPRECATED \
1708 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
@@ -1711,23 +1710,22 @@
1710 -DSQLITE_USE_MALLOC_H \
1711 -DSQLITE_USE_MSIZE
1712
1713 SHELL_OPTIONS = -Dmain=sqlite3_shell \
1714 -DSQLITE_OMIT_LOAD_EXTENSION=1 \
 
1715 -Dgetenv=fossil_getenv \
1716 -Dfopen=fossil_fopen
1717
1718 $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c win/Makefile.mingw.mistachkin
1719 $(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o
1720
1721 $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
1722 $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o
1723
1724 $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_status.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
1725
1726 $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h win/Makefile.mingw.mistachkin
1727 $(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o
1728
1729 $(OBJDIR)/th.o: $(SRCDIR)/th.c
1730 $(XTCC) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
1731
1732
+10 -10
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -23,12 +23,12 @@
2323
2424
# Uncomment to enable SSL support
2525
# FOSSIL_ENABLE_SSL = 1
2626
2727
!ifdef FOSSIL_ENABLE_SSL
28
-SSLINCDIR = $(B)\compat\openssl-1.0.1e\include
29
-SSLLIBDIR = $(B)\compat\openssl-1.0.1e\out32
28
+SSLINCDIR = $(B)\compat\openssl-1.0.1f\include
29
+SSLLIBDIR = $(B)\compat\openssl-1.0.1f\out32
3030
SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib
3131
!endif
3232
3333
# zlib options
3434
ZINCDIR = $(B)\compat\zlib
@@ -39,16 +39,18 @@
3939
4040
!ifdef FOSSIL_ENABLE_SSL
4141
INCL = $(INCL) -I$(SSLINCDIR)
4242
!endif
4343
44
-CFLAGS = -nologo -MT -O2
44
+CFLAGS = -nologo
4545
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
4646
4747
!ifdef DEBUG
48
-CFLAGS = $(CFLAGS) -Zi
48
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
4949
LDFLAGS = $(LDFLAGS) /DEBUG
50
+!else
51
+CFLAGS = $(CFLAGS) -MT -O2
5052
!endif
5153
5254
BCC = $(CC) $(CFLAGS)
5355
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
5456
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -65,21 +67,19 @@
6567
RCC = $(RCC) -DFOSSIL_ENABLE_SSL=1
6668
LIBS = $(LIBS) $(SSLLIB)
6769
LIBDIR = $(LIBDIR) -LIBPATH:$(SSLLIBDIR)
6870
!endif
6971
70
-SQLITE_OPTIONS = /Dlocaltime=fossil_localtime \
71
- /DSQLITE_OMIT_LOAD_EXTENSION=1 \
72
+SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \
7273
/DSQLITE_ENABLE_LOCKING_STYLE=0 \
7374
/DSQLITE_THREADSAFE=0 \
7475
/DSQLITE_DEFAULT_FILE_FORMAT=4 \
7576
/DSQLITE_OMIT_DEPRECATED \
7677
/DSQLITE_ENABLE_EXPLAIN_COMMENTS
7778
7879
SHELL_OPTIONS = /Dmain=sqlite3_shell \
7980
/DSQLITE_OMIT_LOAD_EXTENSION=1 \
80
- /Dsqlite3_strglob=strglob \
8181
/Dgetenv=fossil_getenv \
8282
/Dfopen=fossil_fopen
8383
8484
SRC = add_.c \
8585
allrepo_.c \
@@ -450,15 +450,15 @@
450450
$(BCC) $**
451451
452452
mkversion$E: $B\src\mkversion.c
453453
$(BCC) $**
454454
455
-$(OX)\shell$O : $(SRCDIR)\shell.c
455
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
456456
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
457457
458
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c
459
- $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $**
458
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
459
+ $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
460460
461461
$(OX)\th$O : $(SRCDIR)\th.c
462462
$(TCC) /Fo$@ -c $**
463463
464464
$(OX)\th_lang$O : $(SRCDIR)\th_lang.c
465465
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -23,12 +23,12 @@
23
24 # Uncomment to enable SSL support
25 # FOSSIL_ENABLE_SSL = 1
26
27 !ifdef FOSSIL_ENABLE_SSL
28 SSLINCDIR = $(B)\compat\openssl-1.0.1e\include
29 SSLLIBDIR = $(B)\compat\openssl-1.0.1e\out32
30 SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib
31 !endif
32
33 # zlib options
34 ZINCDIR = $(B)\compat\zlib
@@ -39,16 +39,18 @@
39
40 !ifdef FOSSIL_ENABLE_SSL
41 INCL = $(INCL) -I$(SSLINCDIR)
42 !endif
43
44 CFLAGS = -nologo -MT -O2
45 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
46
47 !ifdef DEBUG
48 CFLAGS = $(CFLAGS) -Zi
49 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
50 !endif
51
52 BCC = $(CC) $(CFLAGS)
53 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
54 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -65,21 +67,19 @@
65 RCC = $(RCC) -DFOSSIL_ENABLE_SSL=1
66 LIBS = $(LIBS) $(SSLLIB)
67 LIBDIR = $(LIBDIR) -LIBPATH:$(SSLLIBDIR)
68 !endif
69
70 SQLITE_OPTIONS = /Dlocaltime=fossil_localtime \
71 /DSQLITE_OMIT_LOAD_EXTENSION=1 \
72 /DSQLITE_ENABLE_LOCKING_STYLE=0 \
73 /DSQLITE_THREADSAFE=0 \
74 /DSQLITE_DEFAULT_FILE_FORMAT=4 \
75 /DSQLITE_OMIT_DEPRECATED \
76 /DSQLITE_ENABLE_EXPLAIN_COMMENTS
77
78 SHELL_OPTIONS = /Dmain=sqlite3_shell \
79 /DSQLITE_OMIT_LOAD_EXTENSION=1 \
80 /Dsqlite3_strglob=strglob \
81 /Dgetenv=fossil_getenv \
82 /Dfopen=fossil_fopen
83
84 SRC = add_.c \
85 allrepo_.c \
@@ -450,15 +450,15 @@
450 $(BCC) $**
451
452 mkversion$E: $B\src\mkversion.c
453 $(BCC) $**
454
455 $(OX)\shell$O : $(SRCDIR)\shell.c
456 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
457
458 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c
459 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $**
460
461 $(OX)\th$O : $(SRCDIR)\th.c
462 $(TCC) /Fo$@ -c $**
463
464 $(OX)\th_lang$O : $(SRCDIR)\th_lang.c
465
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -23,12 +23,12 @@
23
24 # Uncomment to enable SSL support
25 # FOSSIL_ENABLE_SSL = 1
26
27 !ifdef FOSSIL_ENABLE_SSL
28 SSLINCDIR = $(B)\compat\openssl-1.0.1f\include
29 SSLLIBDIR = $(B)\compat\openssl-1.0.1f\out32
30 SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib
31 !endif
32
33 # zlib options
34 ZINCDIR = $(B)\compat\zlib
@@ -39,16 +39,18 @@
39
40 !ifdef FOSSIL_ENABLE_SSL
41 INCL = $(INCL) -I$(SSLINCDIR)
42 !endif
43
44 CFLAGS = -nologo
45 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
46
47 !ifdef DEBUG
48 CFLAGS = $(CFLAGS) -Zi -MTd -Od
49 LDFLAGS = $(LDFLAGS) /DEBUG
50 !else
51 CFLAGS = $(CFLAGS) -MT -O2
52 !endif
53
54 BCC = $(CC) $(CFLAGS)
55 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
56 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -65,21 +67,19 @@
67 RCC = $(RCC) -DFOSSIL_ENABLE_SSL=1
68 LIBS = $(LIBS) $(SSLLIB)
69 LIBDIR = $(LIBDIR) -LIBPATH:$(SSLLIBDIR)
70 !endif
71
72 SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \
 
73 /DSQLITE_ENABLE_LOCKING_STYLE=0 \
74 /DSQLITE_THREADSAFE=0 \
75 /DSQLITE_DEFAULT_FILE_FORMAT=4 \
76 /DSQLITE_OMIT_DEPRECATED \
77 /DSQLITE_ENABLE_EXPLAIN_COMMENTS
78
79 SHELL_OPTIONS = /Dmain=sqlite3_shell \
80 /DSQLITE_OMIT_LOAD_EXTENSION=1 \
 
81 /Dgetenv=fossil_getenv \
82 /Dfopen=fossil_fopen
83
84 SRC = add_.c \
85 allrepo_.c \
@@ -450,15 +450,15 @@
450 $(BCC) $**
451
452 mkversion$E: $B\src\mkversion.c
453 $(BCC) $**
454
455 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
456 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
457
458 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
459 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
460
461 $(OX)\th$O : $(SRCDIR)\th.c
462 $(TCC) /Fo$@ -c $**
463
464 $(OX)\th_lang$O : $(SRCDIR)\th_lang.c
465
+22 -2
--- www/build.wiki
+++ www/build.wiki
@@ -17,12 +17,12 @@
1717
<p>Building and installing is very simple. Three steps:</p>
1818
1919
<ol>
2020
<li> Download and unpack a source tarball or ZIP.
2121
<li> <b>./configure; make</b>
22
-<li> Move or copy the resulting "fossil" executable to someplace
23
- on your $PATH.
22
+<li> Move the resulting "fossil" or "fossil.exe" executable to someplace on
23
+your $PATH.
2424
</ol>
2525
2626
<p><hr>
2727
2828
<h2>1.0 Obtaining The Source Code</h2>
@@ -50,10 +50,30 @@
5050
<li><p>Finally, click on one of the
5151
"Zip Archive" or "Tarball" links, according to your preference.
5252
These link will build a ZIP archive or a gzip-compressed tarball of the
5353
complete source code and download it to your browser.
5454
</ol>
55
+
56
+<h2>Aside: Is it really safe to use an unreleased development version of
57
+the Fossil source code?</h2>
58
+
59
+Yes! Any check-in on the
60
+[/timeline?t=trunk | trunk branch] of the Fossil
61
+[http://fossil-scm.org/fossil/timeline | Fossil self-hosting repository]
62
+will work fine. (Dodgy code is always on a branch.) In the unlikely
63
+event that you pick a version with a serious bug, it still won't
64
+clobber your files. Fossil uses several
65
+[./selfcheck.wiki | self-checks] prior to committing any
66
+repository change that prevent loss-of-work due to bugs.
67
+
68
+The Fossil [./selfhost.wiki | self-hosting repositories], especially
69
+the one at [http://www.fossil-scm.org/fossil], usually run a version
70
+of trunk that is less than a week or two old. Look at the bottom
71
+right-hand corner of this screen (to the right of "This page was
72
+generated in...") to see exactly which version of Fossil is
73
+rendering this page. It is always safe to use whatever version
74
+of the Fossil code you find running on the main Fossil website.
5575
5676
<h2>2.0 Compiling</h2>
5777
5878
<ol>
5979
<li value="5">
6080
--- www/build.wiki
+++ www/build.wiki
@@ -17,12 +17,12 @@
17 <p>Building and installing is very simple. Three steps:</p>
18
19 <ol>
20 <li> Download and unpack a source tarball or ZIP.
21 <li> <b>./configure; make</b>
22 <li> Move or copy the resulting "fossil" executable to someplace
23 on your $PATH.
24 </ol>
25
26 <p><hr>
27
28 <h2>1.0 Obtaining The Source Code</h2>
@@ -50,10 +50,30 @@
50 <li><p>Finally, click on one of the
51 "Zip Archive" or "Tarball" links, according to your preference.
52 These link will build a ZIP archive or a gzip-compressed tarball of the
53 complete source code and download it to your browser.
54 </ol>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
56 <h2>2.0 Compiling</h2>
57
58 <ol>
59 <li value="5">
60
--- www/build.wiki
+++ www/build.wiki
@@ -17,12 +17,12 @@
17 <p>Building and installing is very simple. Three steps:</p>
18
19 <ol>
20 <li> Download and unpack a source tarball or ZIP.
21 <li> <b>./configure; make</b>
22 <li> Move the resulting "fossil" or "fossil.exe" executable to someplace on
23 your $PATH.
24 </ol>
25
26 <p><hr>
27
28 <h2>1.0 Obtaining The Source Code</h2>
@@ -50,10 +50,30 @@
50 <li><p>Finally, click on one of the
51 "Zip Archive" or "Tarball" links, according to your preference.
52 These link will build a ZIP archive or a gzip-compressed tarball of the
53 complete source code and download it to your browser.
54 </ol>
55
56 <h2>Aside: Is it really safe to use an unreleased development version of
57 the Fossil source code?</h2>
58
59 Yes! Any check-in on the
60 [/timeline?t=trunk | trunk branch] of the Fossil
61 [http://fossil-scm.org/fossil/timeline | Fossil self-hosting repository]
62 will work fine. (Dodgy code is always on a branch.) In the unlikely
63 event that you pick a version with a serious bug, it still won't
64 clobber your files. Fossil uses several
65 [./selfcheck.wiki | self-checks] prior to committing any
66 repository change that prevent loss-of-work due to bugs.
67
68 The Fossil [./selfhost.wiki | self-hosting repositories], especially
69 the one at [http://www.fossil-scm.org/fossil], usually run a version
70 of trunk that is less than a week or two old. Look at the bottom
71 right-hand corner of this screen (to the right of "This page was
72 generated in...") to see exactly which version of Fossil is
73 rendering this page. It is always safe to use whatever version
74 of the Fossil code you find running on the main Fossil website.
75
76 <h2>2.0 Compiling</h2>
77
78 <ol>
79 <li value="5">
80
--- www/changes.wiki
+++ www/changes.wiki
@@ -32,10 +32,28 @@
3232
which does not store the URL or password when cloning.
3333
* Modify [/help?cmd=ui | fossil ui] to respect "default user" in an open
3434
repository.
3535
* Fossil now hides check-ins that have the "hidden" tag in timeline webpages.
3636
* Enhance <tt>/ci_edit</tt> page to add the "hidden" tag to check-ins.
37
+ * Advanced possibilities for commit and ticket change notifications over
38
+ http using TH1 scripting.
39
+ * Add --sha1sum and --integrate options
40
+ to the "[/help?cmd=commit | fossil commit]" command.
41
+ * Add the "clean" and "extra" subcommands to the
42
+ "[/help?cmd=all | fossil all]" command
43
+ * Add the --whatif option to "[/help?cmd=clean|fossil clean]" that works the
44
+ same as "--dry-run",
45
+ so that the name does not collide with the --dry-run option of "fossil all".
46
+ * Provide a configuration option to show dates on the web timeline
47
+ as "YYMMMDD HH:MM"
48
+ * Add an option to the "stats" webpage that allows an administrator to see
49
+ the current repository schema.
50
+ * Enhancements to the "[/help?cmd=/vdiff|/vdiff]" webpage for more difference
51
+ display options.
52
+ * Added the "[/tree?ci=trunk&expand | /tree]" webpage as an alternative
53
+ to "/dir" and make it the default way of showing file lists.
54
+ * Send gzipped HTTP responses to clients that support it.
3755
3856
<h2>Changes For Version 1.27 (2013-09-11)</h2>
3957
* Enhance the [/help?cmd=changes | fossil changes],
4058
[/help?cmd=clean | fossil clean], [/help?cmd=extras | fossil extras],
4159
[/help?cmd=ls | fossil ls] and [/help?cmd=status | fossil status] commands
4260
--- www/changes.wiki
+++ www/changes.wiki
@@ -32,10 +32,28 @@
32 which does not store the URL or password when cloning.
33 * Modify [/help?cmd=ui | fossil ui] to respect "default user" in an open
34 repository.
35 * Fossil now hides check-ins that have the "hidden" tag in timeline webpages.
36 * Enhance <tt>/ci_edit</tt> page to add the "hidden" tag to check-ins.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
38 <h2>Changes For Version 1.27 (2013-09-11)</h2>
39 * Enhance the [/help?cmd=changes | fossil changes],
40 [/help?cmd=clean | fossil clean], [/help?cmd=extras | fossil extras],
41 [/help?cmd=ls | fossil ls] and [/help?cmd=status | fossil status] commands
42
--- www/changes.wiki
+++ www/changes.wiki
@@ -32,10 +32,28 @@
32 which does not store the URL or password when cloning.
33 * Modify [/help?cmd=ui | fossil ui] to respect "default user" in an open
34 repository.
35 * Fossil now hides check-ins that have the "hidden" tag in timeline webpages.
36 * Enhance <tt>/ci_edit</tt> page to add the "hidden" tag to check-ins.
37 * Advanced possibilities for commit and ticket change notifications over
38 http using TH1 scripting.
39 * Add --sha1sum and --integrate options
40 to the "[/help?cmd=commit | fossil commit]" command.
41 * Add the "clean" and "extra" subcommands to the
42 "[/help?cmd=all | fossil all]" command
43 * Add the --whatif option to "[/help?cmd=clean|fossil clean]" that works the
44 same as "--dry-run",
45 so that the name does not collide with the --dry-run option of "fossil all".
46 * Provide a configuration option to show dates on the web timeline
47 as "YYMMMDD HH:MM"
48 * Add an option to the "stats" webpage that allows an administrator to see
49 the current repository schema.
50 * Enhancements to the "[/help?cmd=/vdiff|/vdiff]" webpage for more difference
51 display options.
52 * Added the "[/tree?ci=trunk&expand | /tree]" webpage as an alternative
53 to "/dir" and make it the default way of showing file lists.
54 * Send gzipped HTTP responses to clients that support it.
55
56 <h2>Changes For Version 1.27 (2013-09-11)</h2>
57 * Enhance the [/help?cmd=changes | fossil changes],
58 [/help?cmd=clean | fossil clean], [/help?cmd=extras | fossil extras],
59 [/help?cmd=ls | fossil ls] and [/help?cmd=status | fossil status] commands
60
--- www/makefile.wiki
+++ www/makefile.wiki
@@ -203,23 +203,22 @@
203203
together in a final step.
204204
205205
Some files require special C-preprocessor macro definitions.
206206
When compiling sqlite.c, the following macros are recommended:
207207
208
- * -Dlocaltime=fossil_localtime
209208
* -DSQLITE_OMIT_LOAD_EXTENSION=1
210209
* -DSQLITE_ENABLE_LOCKING_STYLE=0
211210
* -DSQLITE_THREADSAFE=0
212211
* -DSQLITE_DEFAULT_FILE_FORMAT=4
212
+ * -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
213213
214
-The first and second symbol definitions above are required; the others
215
-are merely recommended. The "localtime()" library function in SQLite must
216
-be redefined to invoke fossil_localtime() instead. The fossil_localtime()
217
-routine will invoke either gmtime() or localtime() depending on the
218
-"Use UTC" setting for the fossil repository. Extension loading is omitted
214
+The first symbol definition above is required; the others
215
+are merely recommended. Extension loading is omitted
219216
as a security measure. Fossil is single-threaded so mutexing is disabled
220
-in SQLite as a performance enhancement.
217
+in SQLite as a performance enhancement. The SQLITE_ENABLE_EXPLAIN_COMMENTS
218
+option makes the output of "EXPLAIN" queries in the
219
+"[/help?cmd=sqlite3|fossil sql]" command much more readable.
221220
222221
When compiling the shell.c source file, these macros are required:
223222
224223
* -Dmain=sqlite3_main
225224
* -DSQLITE_OMIT_LOAD_EXTENSION=1
226225
--- www/makefile.wiki
+++ www/makefile.wiki
@@ -203,23 +203,22 @@
203 together in a final step.
204
205 Some files require special C-preprocessor macro definitions.
206 When compiling sqlite.c, the following macros are recommended:
207
208 * -Dlocaltime=fossil_localtime
209 * -DSQLITE_OMIT_LOAD_EXTENSION=1
210 * -DSQLITE_ENABLE_LOCKING_STYLE=0
211 * -DSQLITE_THREADSAFE=0
212 * -DSQLITE_DEFAULT_FILE_FORMAT=4
 
213
214 The first and second symbol definitions above are required; the others
215 are merely recommended. The "localtime()" library function in SQLite must
216 be redefined to invoke fossil_localtime() instead. The fossil_localtime()
217 routine will invoke either gmtime() or localtime() depending on the
218 "Use UTC" setting for the fossil repository. Extension loading is omitted
219 as a security measure. Fossil is single-threaded so mutexing is disabled
220 in SQLite as a performance enhancement.
 
 
221
222 When compiling the shell.c source file, these macros are required:
223
224 * -Dmain=sqlite3_main
225 * -DSQLITE_OMIT_LOAD_EXTENSION=1
226
--- www/makefile.wiki
+++ www/makefile.wiki
@@ -203,23 +203,22 @@
203 together in a final step.
204
205 Some files require special C-preprocessor macro definitions.
206 When compiling sqlite.c, the following macros are recommended:
207
 
208 * -DSQLITE_OMIT_LOAD_EXTENSION=1
209 * -DSQLITE_ENABLE_LOCKING_STYLE=0
210 * -DSQLITE_THREADSAFE=0
211 * -DSQLITE_DEFAULT_FILE_FORMAT=4
212 * -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
213
214 The first symbol definition above is required; the others
215 are merely recommended. Extension loading is omitted
 
 
 
216 as a security measure. Fossil is single-threaded so mutexing is disabled
217 in SQLite as a performance enhancement. The SQLITE_ENABLE_EXPLAIN_COMMENTS
218 option makes the output of "EXPLAIN" queries in the
219 "[/help?cmd=sqlite3|fossil sql]" command much more readable.
220
221 When compiling the shell.c source file, these macros are required:
222
223 * -Dmain=sqlite3_main
224 * -DSQLITE_OMIT_LOAD_EXTENSION=1
225

Keyboard Shortcuts

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