Fossil SCM
merge trunk
Commit
02a0e8890e34d6f88ed3aad8419e2d840dc98362
Parent
8e8e3a11bc9665c…
43 files changed
+2
-2
+2
-2
+25
-1
+13
-3
+4
-4
+3
+2
-2
+6
-1
+6
-1
+5
-5
+1
-1
+2
-2
+2
-2
+12
-13
+9
-8
+17
-15
+46
-46
+6
-6
+2
-2
+7
-2
+16
+16
+39
-12
+1
-1
+1
-1
+5
-1
+204
-111
+204
-111
+1
-1
+1
-1
+1
-1
+9
-3
+115
+19
-3
+9
-4
+244
-184
+2
-2
+1
-1
+77
-32
+87
+20
+20
+3
~
src/attach.c
~
src/branch.c
~
src/cgi.c
~
src/checkin.c
~
src/checkout.c
~
src/configure.c
~
src/content.c
~
src/db.c
~
src/db.c
~
src/diff.c
~
src/event.c
~
src/finfo.c
~
src/gzip.c
~
src/http.c
~
src/http_socket.c
~
src/http_ssl.c
~
src/http_transport.c
~
src/info.c
~
src/json_branch.c
~
src/json_config.c
~
src/main.c
~
src/main.c
~
src/manifest.c
~
src/rebuild.c
~
src/setup.c
~
src/shell.c
~
src/sqlite3.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/tag.c
~
src/tar.c
~
src/th.c
~
src/th_main.c
~
src/timeline.c
~
src/tkt.c
~
src/url.c
~
src/user.c
~
src/wiki.c
~
src/xfer.c
~
src/xfersetup.c
~
test/th1.test
~
test/th1.test
~
www/changes.wiki
+2
-2
| --- src/attach.c | ||
| +++ src/attach.c | ||
| @@ -214,11 +214,11 @@ | ||
| 214 | 214 | }else{ |
| 215 | 215 | rid = content_put(pAttach); |
| 216 | 216 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); |
| 217 | 217 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); |
| 218 | 218 | } |
| 219 | - manifest_crosslink(rid, pAttach); | |
| 219 | + manifest_crosslink(rid, pAttach, MC_NONE); | |
| 220 | 220 | } |
| 221 | 221 | |
| 222 | 222 | |
| 223 | 223 | /* |
| 224 | 224 | ** WEBPAGE: attachadd |
| @@ -431,11 +431,11 @@ | ||
| 431 | 431 | blob_appendf(&manifest, "D %s\n", zDate); |
| 432 | 432 | blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody"); |
| 433 | 433 | md5sum_blob(&manifest, &cksum); |
| 434 | 434 | blob_appendf(&manifest, "Z %b\n", &cksum); |
| 435 | 435 | rid = content_put(&manifest); |
| 436 | - manifest_crosslink(rid, &manifest); | |
| 436 | + manifest_crosslink(rid, &manifest, MC_NONE); | |
| 437 | 437 | db_end_transaction(0); |
| 438 | 438 | @ <p>The attachment below has been deleted.</p> |
| 439 | 439 | } |
| 440 | 440 | |
| 441 | 441 | if( P("del") |
| 442 | 442 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -214,11 +214,11 @@ | |
| 214 | }else{ |
| 215 | rid = content_put(pAttach); |
| 216 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); |
| 217 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); |
| 218 | } |
| 219 | manifest_crosslink(rid, pAttach); |
| 220 | } |
| 221 | |
| 222 | |
| 223 | /* |
| 224 | ** WEBPAGE: attachadd |
| @@ -431,11 +431,11 @@ | |
| 431 | blob_appendf(&manifest, "D %s\n", zDate); |
| 432 | blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody"); |
| 433 | md5sum_blob(&manifest, &cksum); |
| 434 | blob_appendf(&manifest, "Z %b\n", &cksum); |
| 435 | rid = content_put(&manifest); |
| 436 | manifest_crosslink(rid, &manifest); |
| 437 | db_end_transaction(0); |
| 438 | @ <p>The attachment below has been deleted.</p> |
| 439 | } |
| 440 | |
| 441 | if( P("del") |
| 442 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -214,11 +214,11 @@ | |
| 214 | }else{ |
| 215 | rid = content_put(pAttach); |
| 216 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); |
| 217 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); |
| 218 | } |
| 219 | manifest_crosslink(rid, pAttach, MC_NONE); |
| 220 | } |
| 221 | |
| 222 | |
| 223 | /* |
| 224 | ** WEBPAGE: attachadd |
| @@ -431,11 +431,11 @@ | |
| 431 | blob_appendf(&manifest, "D %s\n", zDate); |
| 432 | blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody"); |
| 433 | md5sum_blob(&manifest, &cksum); |
| 434 | blob_appendf(&manifest, "Z %b\n", &cksum); |
| 435 | rid = content_put(&manifest); |
| 436 | manifest_crosslink(rid, &manifest, MC_NONE); |
| 437 | db_end_transaction(0); |
| 438 | @ <p>The attachment below has been deleted.</p> |
| 439 | } |
| 440 | |
| 441 | if( P("del") |
| 442 |
+2
-2
| --- src/branch.c | ||
| +++ src/branch.c | ||
| @@ -153,12 +153,12 @@ | ||
| 153 | 153 | brid = content_put_ex(&branch, 0, 0, 0, isPrivate); |
| 154 | 154 | if( brid==0 ){ |
| 155 | 155 | fossil_fatal("trouble committing manifest: %s", g.zErrMsg); |
| 156 | 156 | } |
| 157 | 157 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 158 | - if( manifest_crosslink(brid, &branch)==0 ){ | |
| 159 | - fossil_fatal("unable to install new manifest"); | |
| 158 | + if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ | |
| 159 | + fossil_fatal("%s\n", g.zErrMsg); | |
| 160 | 160 | } |
| 161 | 161 | assert( blob_is_reset(&branch) ); |
| 162 | 162 | content_deltify(rootid, brid, 0); |
| 163 | 163 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid); |
| 164 | 164 | fossil_print("New branch: %s\n", zUuid); |
| 165 | 165 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -153,12 +153,12 @@ | |
| 153 | brid = content_put_ex(&branch, 0, 0, 0, isPrivate); |
| 154 | if( brid==0 ){ |
| 155 | fossil_fatal("trouble committing manifest: %s", g.zErrMsg); |
| 156 | } |
| 157 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 158 | if( manifest_crosslink(brid, &branch)==0 ){ |
| 159 | fossil_fatal("unable to install new manifest"); |
| 160 | } |
| 161 | assert( blob_is_reset(&branch) ); |
| 162 | content_deltify(rootid, brid, 0); |
| 163 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid); |
| 164 | fossil_print("New branch: %s\n", zUuid); |
| 165 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -153,12 +153,12 @@ | |
| 153 | brid = content_put_ex(&branch, 0, 0, 0, isPrivate); |
| 154 | if( brid==0 ){ |
| 155 | fossil_fatal("trouble committing manifest: %s", g.zErrMsg); |
| 156 | } |
| 157 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 158 | if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ |
| 159 | fossil_fatal("%s\n", g.zErrMsg); |
| 160 | } |
| 161 | assert( blob_is_reset(&branch) ); |
| 162 | content_deltify(rootid, brid, 0); |
| 163 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid); |
| 164 | fossil_print("New branch: %s\n", zUuid); |
| 165 |
+25
-1
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -270,10 +270,20 @@ | ||
| 270 | 270 | } |
| 271 | 271 | |
| 272 | 272 | return 0; |
| 273 | 273 | } |
| 274 | 274 | #endif |
| 275 | + | |
| 276 | +/* | |
| 277 | +** Return true if the response should be sent with Content-Encoding: gzip. | |
| 278 | +*/ | |
| 279 | +static int is_gzippable(void){ | |
| 280 | + if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0; | |
| 281 | + return strncmp(zContentType, "text/", 5)==0 | |
| 282 | + || strglob("application/*xml", zContentType) | |
| 283 | + || strglob("application/*javascript", zContentType); | |
| 284 | +} | |
| 275 | 285 | |
| 276 | 286 | /* |
| 277 | 287 | ** Do a normal HTTP reply |
| 278 | 288 | */ |
| 279 | 289 | void cgi_reply(void){ |
| @@ -348,10 +358,22 @@ | ||
| 348 | 358 | cgi_combine_header_and_body(); |
| 349 | 359 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 350 | 360 | } |
| 351 | 361 | |
| 352 | 362 | if( iReplyStatus != 304 ) { |
| 363 | + if( is_gzippable() ){ | |
| 364 | + int i; | |
| 365 | + gzip_begin(0); | |
| 366 | + for( i=0; i<2; i++ ){ | |
| 367 | + int size = blob_size(&cgiContent[i]); | |
| 368 | + if( size>0 ) gzip_step(blob_buffer(&cgiContent[i]), size); | |
| 369 | + blob_reset(&cgiContent[i]); | |
| 370 | + } | |
| 371 | + gzip_finish(&cgiContent[0]); | |
| 372 | + fprintf(g.httpOut, "Content-Encoding: gzip\r\n"); | |
| 373 | + fprintf(g.httpOut, "Vary: Accept-Encoding\r\n"); | |
| 374 | + } | |
| 353 | 375 | total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); |
| 354 | 376 | fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); |
| 355 | 377 | }else{ |
| 356 | 378 | total_size = 0; |
| 357 | 379 | } |
| @@ -1302,11 +1324,13 @@ | ||
| 1302 | 1324 | while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; } |
| 1303 | 1325 | zVal[i] = 0; |
| 1304 | 1326 | for(i=0; zFieldName[i]; i++){ |
| 1305 | 1327 | zFieldName[i] = fossil_tolower(zFieldName[i]); |
| 1306 | 1328 | } |
| 1307 | - if( fossil_strcmp(zFieldName,"content-length:")==0 ){ | |
| 1329 | + if( fossil_strcmp(zFieldName,"accept-encoding:")==0 ){ | |
| 1330 | + cgi_setenv("HTTP_ACCEPT_ENCODING", zVal); | |
| 1331 | + }else if( fossil_strcmp(zFieldName,"content-length:")==0 ){ | |
| 1308 | 1332 | cgi_setenv("CONTENT_LENGTH", zVal); |
| 1309 | 1333 | }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){ |
| 1310 | 1334 | cgi_setenv("CONTENT_TYPE", zVal); |
| 1311 | 1335 | }else if( fossil_strcmp(zFieldName,"cookie:")==0 ){ |
| 1312 | 1336 | cgi_setenv("HTTP_COOKIE", zVal); |
| 1313 | 1337 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -270,10 +270,20 @@ | |
| 270 | } |
| 271 | |
| 272 | return 0; |
| 273 | } |
| 274 | #endif |
| 275 | |
| 276 | /* |
| 277 | ** Do a normal HTTP reply |
| 278 | */ |
| 279 | void cgi_reply(void){ |
| @@ -348,10 +358,22 @@ | |
| 348 | cgi_combine_header_and_body(); |
| 349 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 350 | } |
| 351 | |
| 352 | if( iReplyStatus != 304 ) { |
| 353 | total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); |
| 354 | fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); |
| 355 | }else{ |
| 356 | total_size = 0; |
| 357 | } |
| @@ -1302,11 +1324,13 @@ | |
| 1302 | while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; } |
| 1303 | zVal[i] = 0; |
| 1304 | for(i=0; zFieldName[i]; i++){ |
| 1305 | zFieldName[i] = fossil_tolower(zFieldName[i]); |
| 1306 | } |
| 1307 | if( fossil_strcmp(zFieldName,"content-length:")==0 ){ |
| 1308 | cgi_setenv("CONTENT_LENGTH", zVal); |
| 1309 | }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){ |
| 1310 | cgi_setenv("CONTENT_TYPE", zVal); |
| 1311 | }else if( fossil_strcmp(zFieldName,"cookie:")==0 ){ |
| 1312 | cgi_setenv("HTTP_COOKIE", zVal); |
| 1313 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -270,10 +270,20 @@ | |
| 270 | } |
| 271 | |
| 272 | return 0; |
| 273 | } |
| 274 | #endif |
| 275 | |
| 276 | /* |
| 277 | ** Return true if the response should be sent with Content-Encoding: gzip. |
| 278 | */ |
| 279 | static int is_gzippable(void){ |
| 280 | if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0; |
| 281 | return strncmp(zContentType, "text/", 5)==0 |
| 282 | || strglob("application/*xml", zContentType) |
| 283 | || strglob("application/*javascript", zContentType); |
| 284 | } |
| 285 | |
| 286 | /* |
| 287 | ** Do a normal HTTP reply |
| 288 | */ |
| 289 | void cgi_reply(void){ |
| @@ -348,10 +358,22 @@ | |
| 358 | cgi_combine_header_and_body(); |
| 359 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 360 | } |
| 361 | |
| 362 | if( iReplyStatus != 304 ) { |
| 363 | if( is_gzippable() ){ |
| 364 | int i; |
| 365 | gzip_begin(0); |
| 366 | for( i=0; i<2; i++ ){ |
| 367 | int size = blob_size(&cgiContent[i]); |
| 368 | if( size>0 ) gzip_step(blob_buffer(&cgiContent[i]), size); |
| 369 | blob_reset(&cgiContent[i]); |
| 370 | } |
| 371 | gzip_finish(&cgiContent[0]); |
| 372 | fprintf(g.httpOut, "Content-Encoding: gzip\r\n"); |
| 373 | fprintf(g.httpOut, "Vary: Accept-Encoding\r\n"); |
| 374 | } |
| 375 | total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); |
| 376 | fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); |
| 377 | }else{ |
| 378 | total_size = 0; |
| 379 | } |
| @@ -1302,11 +1324,13 @@ | |
| 1324 | while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; } |
| 1325 | zVal[i] = 0; |
| 1326 | for(i=0; zFieldName[i]; i++){ |
| 1327 | zFieldName[i] = fossil_tolower(zFieldName[i]); |
| 1328 | } |
| 1329 | if( fossil_strcmp(zFieldName,"accept-encoding:")==0 ){ |
| 1330 | cgi_setenv("HTTP_ACCEPT_ENCODING", zVal); |
| 1331 | }else if( fossil_strcmp(zFieldName,"content-length:")==0 ){ |
| 1332 | cgi_setenv("CONTENT_LENGTH", zVal); |
| 1333 | }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){ |
| 1334 | cgi_setenv("CONTENT_TYPE", zVal); |
| 1335 | }else if( fossil_strcmp(zFieldName,"cookie:")==0 ){ |
| 1336 | cgi_setenv("HTTP_COOKIE", zVal); |
| 1337 |
+13
-3
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -1377,11 +1377,14 @@ | ||
| 1377 | 1377 | ** allowed against a closed leaf. |
| 1378 | 1378 | ** |
| 1379 | 1379 | ** The --private option creates a private check-in that is never synced. |
| 1380 | 1380 | ** Children of private check-ins are automatically private. |
| 1381 | 1381 | ** |
| 1382 | -** the --tag option applies the symbolic tag name to the check-in. | |
| 1382 | +** The --tag option applies the symbolic tag name to the check-in. | |
| 1383 | +** | |
| 1384 | +** The --sha1sum option detects edited files by computing each file's | |
| 1385 | +** SHA1 hash rather than just checking for changes to its size or mtime. | |
| 1383 | 1386 | ** |
| 1384 | 1387 | ** Options: |
| 1385 | 1388 | ** --allow-conflict allow unresolved merge conflicts |
| 1386 | 1389 | ** --allow-empty allow a commit with no changes |
| 1387 | 1390 | ** --allow-fork allow the commit to fork |
| @@ -1397,10 +1400,12 @@ | ||
| 1397 | 1400 | ** --mimetype MIMETYPE mimetype of check-in comment |
| 1398 | 1401 | ** -n|--dry-run If given, display instead of run actions |
| 1399 | 1402 | ** --no-warnings omit all warnings about file contents |
| 1400 | 1403 | ** --nosign do not attempt to sign this commit with gpg |
| 1401 | 1404 | ** --private do not sync changes and their descendants |
| 1405 | +** --sha1sum verify file status using SHA1 hashing rather | |
| 1406 | +** than relying on file mtimes | |
| 1402 | 1407 | ** --tag TAG-NAME assign given tag TAG-NAME to the checkin |
| 1403 | 1408 | ** |
| 1404 | 1409 | ** See also: branch, changes, checkout, extra, sync |
| 1405 | 1410 | */ |
| 1406 | 1411 | void commit_cmd(void){ |
| @@ -1410,10 +1415,11 @@ | ||
| 1410 | 1415 | int nvid; /* Blob-id of the new check-in */ |
| 1411 | 1416 | Blob comment; /* Check-in comment */ |
| 1412 | 1417 | const char *zComment; /* Check-in comment */ |
| 1413 | 1418 | Stmt q; /* Various queries */ |
| 1414 | 1419 | char *zUuid; /* UUID of the new check-in */ |
| 1420 | + int useSha1sum = 0; /* True to verify file status using SHA1 hashing */ | |
| 1415 | 1421 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 1416 | 1422 | int isAMerge = 0; /* True if checking in a merge */ |
| 1417 | 1423 | int noWarningFlag = 0; /* True if skipping all warnings */ |
| 1418 | 1424 | int forceFlag = 0; /* Undocumented: Disables all checks */ |
| 1419 | 1425 | int forceDelta = 0; /* Force a delta-manifest */ |
| @@ -1441,10 +1447,11 @@ | ||
| 1441 | 1447 | Blob ans; |
| 1442 | 1448 | char cReply; |
| 1443 | 1449 | |
| 1444 | 1450 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 1445 | 1451 | url_proxy_options(); |
| 1452 | + useSha1sum = find_option("sha1sum", 0, 0)!=0; | |
| 1446 | 1453 | noSign = find_option("nosign",0,0)!=0; |
| 1447 | 1454 | forceDelta = find_option("delta",0,0)!=0; |
| 1448 | 1455 | forceBaseline = find_option("baseline",0,0)!=0; |
| 1449 | 1456 | if( forceDelta && forceBaseline ){ |
| 1450 | 1457 | fossil_fatal("cannot use --delta and --baseline together"); |
| @@ -1586,11 +1593,11 @@ | ||
| 1586 | 1593 | */ |
| 1587 | 1594 | if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ |
| 1588 | 1595 | fossil_fatal("no such user: %s", g.zLogin); |
| 1589 | 1596 | } |
| 1590 | 1597 | |
| 1591 | - hasChanges = unsaved_changes(); | |
| 1598 | + hasChanges = unsaved_changes(useSha1sum ? CKSIG_SHA1 : 0); | |
| 1592 | 1599 | db_begin_transaction(); |
| 1593 | 1600 | db_record_repository_filename(0); |
| 1594 | 1601 | if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){ |
| 1595 | 1602 | fossil_fatal("nothing has changed; use --allow-empty to override"); |
| 1596 | 1603 | } |
| @@ -1816,11 +1823,14 @@ | ||
| 1816 | 1823 | nvid = content_put(&manifest); |
| 1817 | 1824 | if( nvid==0 ){ |
| 1818 | 1825 | fossil_fatal("trouble committing manifest: %s", g.zErrMsg); |
| 1819 | 1826 | } |
| 1820 | 1827 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid); |
| 1821 | - manifest_crosslink(nvid, &manifest); | |
| 1828 | + if( manifest_crosslink(nvid, &manifest, | |
| 1829 | + dryRunFlag ? MC_NONE : MC_PERMIT_HOOKS)==0 ){ | |
| 1830 | + fossil_fatal("%s\n", g.zErrMsg); | |
| 1831 | + } | |
| 1822 | 1832 | assert( blob_is_reset(&manifest) ); |
| 1823 | 1833 | content_deltify(vid, nvid, 0); |
| 1824 | 1834 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); |
| 1825 | 1835 | |
| 1826 | 1836 | db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid" |
| 1827 | 1837 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -1377,11 +1377,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 |
| @@ -1397,10 +1400,12 @@ | |
| 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 +1415,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 +1447,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"); |
| @@ -1586,11 +1593,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 | } |
| @@ -1816,11 +1823,14 @@ | |
| 1816 | nvid = content_put(&manifest); |
| 1817 | if( nvid==0 ){ |
| 1818 | fossil_fatal("trouble committing manifest: %s", g.zErrMsg); |
| 1819 | } |
| 1820 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid); |
| 1821 | manifest_crosslink(nvid, &manifest); |
| 1822 | assert( blob_is_reset(&manifest) ); |
| 1823 | content_deltify(vid, nvid, 0); |
| 1824 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); |
| 1825 | |
| 1826 | db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid" |
| 1827 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -1377,11 +1377,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 | ** The --sha1sum option detects edited files by computing each file's |
| 1385 | ** SHA1 hash rather than just checking for changes to its size or mtime. |
| 1386 | ** |
| 1387 | ** Options: |
| 1388 | ** --allow-conflict allow unresolved merge conflicts |
| 1389 | ** --allow-empty allow a commit with no changes |
| 1390 | ** --allow-fork allow the commit to fork |
| @@ -1397,10 +1400,12 @@ | |
| 1400 | ** --mimetype MIMETYPE mimetype of check-in comment |
| 1401 | ** -n|--dry-run If given, display instead of run actions |
| 1402 | ** --no-warnings omit all warnings about file contents |
| 1403 | ** --nosign do not attempt to sign this commit with gpg |
| 1404 | ** --private do not sync changes and their descendants |
| 1405 | ** --sha1sum verify file status using SHA1 hashing rather |
| 1406 | ** than relying on file mtimes |
| 1407 | ** --tag TAG-NAME assign given tag TAG-NAME to the checkin |
| 1408 | ** |
| 1409 | ** See also: branch, changes, checkout, extra, sync |
| 1410 | */ |
| 1411 | void commit_cmd(void){ |
| @@ -1410,10 +1415,11 @@ | |
| 1415 | int nvid; /* Blob-id of the new check-in */ |
| 1416 | Blob comment; /* Check-in comment */ |
| 1417 | const char *zComment; /* Check-in comment */ |
| 1418 | Stmt q; /* Various queries */ |
| 1419 | char *zUuid; /* UUID of the new check-in */ |
| 1420 | int useSha1sum = 0; /* True to verify file status using SHA1 hashing */ |
| 1421 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 1422 | int isAMerge = 0; /* True if checking in a merge */ |
| 1423 | int noWarningFlag = 0; /* True if skipping all warnings */ |
| 1424 | int forceFlag = 0; /* Undocumented: Disables all checks */ |
| 1425 | int forceDelta = 0; /* Force a delta-manifest */ |
| @@ -1441,10 +1447,11 @@ | |
| 1447 | Blob ans; |
| 1448 | char cReply; |
| 1449 | |
| 1450 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 1451 | url_proxy_options(); |
| 1452 | useSha1sum = find_option("sha1sum", 0, 0)!=0; |
| 1453 | noSign = find_option("nosign",0,0)!=0; |
| 1454 | forceDelta = find_option("delta",0,0)!=0; |
| 1455 | forceBaseline = find_option("baseline",0,0)!=0; |
| 1456 | if( forceDelta && forceBaseline ){ |
| 1457 | fossil_fatal("cannot use --delta and --baseline together"); |
| @@ -1586,11 +1593,11 @@ | |
| 1593 | */ |
| 1594 | if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ |
| 1595 | fossil_fatal("no such user: %s", g.zLogin); |
| 1596 | } |
| 1597 | |
| 1598 | hasChanges = unsaved_changes(useSha1sum ? CKSIG_SHA1 : 0); |
| 1599 | db_begin_transaction(); |
| 1600 | db_record_repository_filename(0); |
| 1601 | if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){ |
| 1602 | fossil_fatal("nothing has changed; use --allow-empty to override"); |
| 1603 | } |
| @@ -1816,11 +1823,14 @@ | |
| 1823 | nvid = content_put(&manifest); |
| 1824 | if( nvid==0 ){ |
| 1825 | fossil_fatal("trouble committing manifest: %s", g.zErrMsg); |
| 1826 | } |
| 1827 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid); |
| 1828 | if( manifest_crosslink(nvid, &manifest, |
| 1829 | dryRunFlag ? MC_NONE : MC_PERMIT_HOOKS)==0 ){ |
| 1830 | fossil_fatal("%s\n", g.zErrMsg); |
| 1831 | } |
| 1832 | assert( blob_is_reset(&manifest) ); |
| 1833 | content_deltify(vid, nvid, 0); |
| 1834 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); |
| 1835 | |
| 1836 | db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid" |
| 1837 |
+4
-4
| --- src/checkout.c | ||
| +++ src/checkout.c | ||
| @@ -28,16 +28,16 @@ | ||
| 28 | 28 | ** |
| 29 | 29 | ** 0: There is an existing checkout but it is unmodified |
| 30 | 30 | ** 1: There is a modified checkout - there are unsaved changes |
| 31 | 31 | ** 2: There is no existing checkout |
| 32 | 32 | */ |
| 33 | -int unsaved_changes(void){ | |
| 33 | +int unsaved_changes(unsigned int cksigFlags){ | |
| 34 | 34 | int vid; |
| 35 | 35 | db_must_be_within_tree(); |
| 36 | 36 | vid = db_lget_int("checkout",0); |
| 37 | 37 | if( vid==0 ) return 2; |
| 38 | - vfile_check_signature(vid, CKSIG_ENOTFILE); | |
| 38 | + vfile_check_signature(vid, cksigFlags|CKSIG_ENOTFILE); | |
| 39 | 39 | return db_exists("SELECT 1 FROM vfile WHERE chnged" |
| 40 | 40 | " OR coalesce(origname!=pathname,0)"); |
| 41 | 41 | } |
| 42 | 42 | |
| 43 | 43 | /* |
| @@ -200,11 +200,11 @@ | ||
| 200 | 200 | latestFlag = find_option("latest",0,0)!=0; |
| 201 | 201 | promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0; |
| 202 | 202 | if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){ |
| 203 | 203 | usage("VERSION|--latest ?--force? ?--keep?"); |
| 204 | 204 | } |
| 205 | - if( !forceFlag && unsaved_changes()==1 ){ | |
| 205 | + if( !forceFlag && unsaved_changes(0)==1 ){ | |
| 206 | 206 | fossil_fatal("there are unsaved changes in the current checkout"); |
| 207 | 207 | } |
| 208 | 208 | if( forceFlag ){ |
| 209 | 209 | db_multi_exec("DELETE FROM vfile"); |
| 210 | 210 | prior = 0; |
| @@ -288,11 +288,11 @@ | ||
| 288 | 288 | ** See also: open |
| 289 | 289 | */ |
| 290 | 290 | void close_cmd(void){ |
| 291 | 291 | int forceFlag = find_option("force","f",0)!=0; |
| 292 | 292 | db_must_be_within_tree(); |
| 293 | - if( !forceFlag && unsaved_changes()==1 ){ | |
| 293 | + if( !forceFlag && unsaved_changes(0)==1 ){ | |
| 294 | 294 | fossil_fatal("there are unsaved changes in the current checkout"); |
| 295 | 295 | } |
| 296 | 296 | if( !forceFlag |
| 297 | 297 | && db_exists("SELECT 1 FROM %s.sqlite_master WHERE name='stash'", |
| 298 | 298 | db_name("localdb")) |
| 299 | 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(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 |
+3
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -98,10 +98,11 @@ | ||
| 98 | 98 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 99 | 99 | { "adunit", CONFIGSET_SKIN }, |
| 100 | 100 | { "adunit-omit-if-admin", CONFIGSET_SKIN }, |
| 101 | 101 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 102 | 102 | { "th1-setup", CONFIGSET_TH1 }, |
| 103 | + { "th1-uri-regexp", CONFIGSET_TH1 }, | |
| 103 | 104 | |
| 104 | 105 | #ifdef FOSSIL_ENABLE_TCL |
| 105 | 106 | { "tcl", CONFIGSET_TH1 }, |
| 106 | 107 | { "tcl-setup", CONFIGSET_TH1 }, |
| 107 | 108 | #endif |
| @@ -138,10 +139,12 @@ | ||
| 138 | 139 | |
| 139 | 140 | { "@shun", CONFIGSET_SHUN }, |
| 140 | 141 | |
| 141 | 142 | { "xfer-common-script", CONFIGSET_XFER }, |
| 142 | 143 | { "xfer-push-script", CONFIGSET_XFER }, |
| 144 | + { "xfer-commit-script", CONFIGSET_XFER }, | |
| 145 | + { "xfer-ticket-script", CONFIGSET_XFER }, | |
| 143 | 146 | |
| 144 | 147 | }; |
| 145 | 148 | static int iConfig = 0; |
| 146 | 149 | |
| 147 | 150 | /* |
| 148 | 151 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -98,10 +98,11 @@ | |
| 98 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 99 | { "adunit", CONFIGSET_SKIN }, |
| 100 | { "adunit-omit-if-admin", CONFIGSET_SKIN }, |
| 101 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 102 | { "th1-setup", CONFIGSET_TH1 }, |
| 103 | |
| 104 | #ifdef FOSSIL_ENABLE_TCL |
| 105 | { "tcl", CONFIGSET_TH1 }, |
| 106 | { "tcl-setup", CONFIGSET_TH1 }, |
| 107 | #endif |
| @@ -138,10 +139,12 @@ | |
| 138 | |
| 139 | { "@shun", CONFIGSET_SHUN }, |
| 140 | |
| 141 | { "xfer-common-script", CONFIGSET_XFER }, |
| 142 | { "xfer-push-script", CONFIGSET_XFER }, |
| 143 | |
| 144 | }; |
| 145 | static int iConfig = 0; |
| 146 | |
| 147 | /* |
| 148 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -98,10 +98,11 @@ | |
| 98 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 99 | { "adunit", CONFIGSET_SKIN }, |
| 100 | { "adunit-omit-if-admin", CONFIGSET_SKIN }, |
| 101 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 102 | { "th1-setup", CONFIGSET_TH1 }, |
| 103 | { "th1-uri-regexp", CONFIGSET_TH1 }, |
| 104 | |
| 105 | #ifdef FOSSIL_ENABLE_TCL |
| 106 | { "tcl", CONFIGSET_TH1 }, |
| 107 | { "tcl-setup", CONFIGSET_TH1 }, |
| 108 | #endif |
| @@ -138,10 +139,12 @@ | |
| 139 | |
| 140 | { "@shun", CONFIGSET_SHUN }, |
| 141 | |
| 142 | { "xfer-common-script", CONFIGSET_XFER }, |
| 143 | { "xfer-push-script", CONFIGSET_XFER }, |
| 144 | { "xfer-commit-script", CONFIGSET_XFER }, |
| 145 | { "xfer-ticket-script", CONFIGSET_XFER }, |
| 146 | |
| 147 | }; |
| 148 | static int iConfig = 0; |
| 149 | |
| 150 | /* |
| 151 |
+2
-2
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -388,11 +388,11 @@ | ||
| 388 | 388 | int i; |
| 389 | 389 | |
| 390 | 390 | /* Parse the object rid itself */ |
| 391 | 391 | if( linkFlag ){ |
| 392 | 392 | content_get(rid, &content); |
| 393 | - manifest_crosslink(rid, &content); | |
| 393 | + manifest_crosslink(rid, &content, MC_NONE); | |
| 394 | 394 | assert( blob_is_reset(&content) ); |
| 395 | 395 | } |
| 396 | 396 | |
| 397 | 397 | /* Parse all delta-manifests that depend on baseline-manifest rid */ |
| 398 | 398 | db_prepare(&q, "SELECT rid FROM orphan WHERE baseline=%d", rid); |
| @@ -405,11 +405,11 @@ | ||
| 405 | 405 | aChild[nChildUsed++] = child; |
| 406 | 406 | } |
| 407 | 407 | db_finalize(&q); |
| 408 | 408 | for(i=0; i<nChildUsed; i++){ |
| 409 | 409 | content_get(aChild[i], &content); |
| 410 | - manifest_crosslink(aChild[i], &content); | |
| 410 | + manifest_crosslink(aChild[i], &content, MC_NONE); | |
| 411 | 411 | assert( blob_is_reset(&content) ); |
| 412 | 412 | } |
| 413 | 413 | if( nChildUsed ){ |
| 414 | 414 | db_multi_exec("DELETE FROM orphan WHERE baseline=%d", rid); |
| 415 | 415 | } |
| 416 | 416 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -388,11 +388,11 @@ | |
| 388 | int i; |
| 389 | |
| 390 | /* Parse the object rid itself */ |
| 391 | if( linkFlag ){ |
| 392 | content_get(rid, &content); |
| 393 | manifest_crosslink(rid, &content); |
| 394 | assert( blob_is_reset(&content) ); |
| 395 | } |
| 396 | |
| 397 | /* Parse all delta-manifests that depend on baseline-manifest rid */ |
| 398 | db_prepare(&q, "SELECT rid FROM orphan WHERE baseline=%d", rid); |
| @@ -405,11 +405,11 @@ | |
| 405 | aChild[nChildUsed++] = child; |
| 406 | } |
| 407 | db_finalize(&q); |
| 408 | for(i=0; i<nChildUsed; i++){ |
| 409 | content_get(aChild[i], &content); |
| 410 | manifest_crosslink(aChild[i], &content); |
| 411 | assert( blob_is_reset(&content) ); |
| 412 | } |
| 413 | if( nChildUsed ){ |
| 414 | db_multi_exec("DELETE FROM orphan WHERE baseline=%d", rid); |
| 415 | } |
| 416 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -388,11 +388,11 @@ | |
| 388 | int i; |
| 389 | |
| 390 | /* Parse the object rid itself */ |
| 391 | if( linkFlag ){ |
| 392 | content_get(rid, &content); |
| 393 | manifest_crosslink(rid, &content, MC_NONE); |
| 394 | assert( blob_is_reset(&content) ); |
| 395 | } |
| 396 | |
| 397 | /* Parse all delta-manifests that depend on baseline-manifest rid */ |
| 398 | db_prepare(&q, "SELECT rid FROM orphan WHERE baseline=%d", rid); |
| @@ -405,11 +405,11 @@ | |
| 405 | aChild[nChildUsed++] = child; |
| 406 | } |
| 407 | db_finalize(&q); |
| 408 | for(i=0; i<nChildUsed; i++){ |
| 409 | content_get(aChild[i], &content); |
| 410 | manifest_crosslink(aChild[i], &content, MC_NONE); |
| 411 | assert( blob_is_reset(&content) ); |
| 412 | } |
| 413 | if( nChildUsed ){ |
| 414 | db_multi_exec("DELETE FROM orphan WHERE baseline=%d", rid); |
| 415 | } |
| 416 |
M
src/db.c
+6
-1
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1418,11 +1418,11 @@ | ||
| 1418 | 1418 | blob_appendf(&manifest, "U %F\n", g.zLogin); |
| 1419 | 1419 | md5sum_blob(&manifest, &hash); |
| 1420 | 1420 | blob_appendf(&manifest, "Z %b\n", &hash); |
| 1421 | 1421 | blob_reset(&hash); |
| 1422 | 1422 | rid = content_put(&manifest); |
| 1423 | - manifest_crosslink(rid, &manifest); | |
| 1423 | + manifest_crosslink(rid, &manifest, MC_NONE); | |
| 1424 | 1424 | } |
| 1425 | 1425 | } |
| 1426 | 1426 | |
| 1427 | 1427 | /* |
| 1428 | 1428 | ** COMMAND: new* |
| @@ -2158,10 +2158,11 @@ | ||
| 2158 | 2158 | #ifdef FOSSIL_ENABLE_TCL |
| 2159 | 2159 | { "tcl", 0, 0, 0, "off" }, |
| 2160 | 2160 | { "tcl-setup", 0, 40, 0, "" }, |
| 2161 | 2161 | #endif |
| 2162 | 2162 | { "th1-setup", 0, 40, 0, "" }, |
| 2163 | + { "th1-uri-regexp",0, 40, 0, "" }, | |
| 2163 | 2164 | { "web-browser", 0, 32, 0, "" }, |
| 2164 | 2165 | { "white-foreground", 0, 0, 0, "off" }, |
| 2165 | 2166 | { 0,0,0,0,0 } |
| 2166 | 2167 | }; |
| 2167 | 2168 | |
| @@ -2354,10 +2355,14 @@ | ||
| 2354 | 2355 | ** is empty and no extra setup is performed. |
| 2355 | 2356 | ** |
| 2356 | 2357 | ** th1-setup This is the setup script to be evaluated after creating |
| 2357 | 2358 | ** and initializing the TH1 interpreter. By default, this |
| 2358 | 2359 | ** is empty and no extra setup is performed. |
| 2360 | +** | |
| 2361 | +** th1-uri-regexp Specify which URI's are allowed in HTTP requests from | |
| 2362 | +** TH1 scripts. If empty, no HTTP requests are allowed | |
| 2363 | +** whatsoever. The default is an empty string. | |
| 2359 | 2364 | ** |
| 2360 | 2365 | ** web-browser A shell command used to launch your preferred |
| 2361 | 2366 | ** web browser when given a URL as an argument. |
| 2362 | 2367 | ** Defaults to "start" on windows, "open" on Mac, |
| 2363 | 2368 | ** and "firefox" on Unix. |
| 2364 | 2369 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1418,11 +1418,11 @@ | |
| 1418 | blob_appendf(&manifest, "U %F\n", g.zLogin); |
| 1419 | md5sum_blob(&manifest, &hash); |
| 1420 | blob_appendf(&manifest, "Z %b\n", &hash); |
| 1421 | blob_reset(&hash); |
| 1422 | rid = content_put(&manifest); |
| 1423 | manifest_crosslink(rid, &manifest); |
| 1424 | } |
| 1425 | } |
| 1426 | |
| 1427 | /* |
| 1428 | ** COMMAND: new* |
| @@ -2158,10 +2158,11 @@ | |
| 2158 | #ifdef FOSSIL_ENABLE_TCL |
| 2159 | { "tcl", 0, 0, 0, "off" }, |
| 2160 | { "tcl-setup", 0, 40, 0, "" }, |
| 2161 | #endif |
| 2162 | { "th1-setup", 0, 40, 0, "" }, |
| 2163 | { "web-browser", 0, 32, 0, "" }, |
| 2164 | { "white-foreground", 0, 0, 0, "off" }, |
| 2165 | { 0,0,0,0,0 } |
| 2166 | }; |
| 2167 | |
| @@ -2354,10 +2355,14 @@ | |
| 2354 | ** is empty and no extra setup is performed. |
| 2355 | ** |
| 2356 | ** th1-setup This is the setup script to be evaluated after creating |
| 2357 | ** and initializing the TH1 interpreter. By default, this |
| 2358 | ** is empty and no extra setup is performed. |
| 2359 | ** |
| 2360 | ** web-browser A shell command used to launch your preferred |
| 2361 | ** web browser when given a URL as an argument. |
| 2362 | ** Defaults to "start" on windows, "open" on Mac, |
| 2363 | ** and "firefox" on Unix. |
| 2364 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1418,11 +1418,11 @@ | |
| 1418 | blob_appendf(&manifest, "U %F\n", g.zLogin); |
| 1419 | md5sum_blob(&manifest, &hash); |
| 1420 | blob_appendf(&manifest, "Z %b\n", &hash); |
| 1421 | blob_reset(&hash); |
| 1422 | rid = content_put(&manifest); |
| 1423 | manifest_crosslink(rid, &manifest, MC_NONE); |
| 1424 | } |
| 1425 | } |
| 1426 | |
| 1427 | /* |
| 1428 | ** COMMAND: new* |
| @@ -2158,10 +2158,11 @@ | |
| 2158 | #ifdef FOSSIL_ENABLE_TCL |
| 2159 | { "tcl", 0, 0, 0, "off" }, |
| 2160 | { "tcl-setup", 0, 40, 0, "" }, |
| 2161 | #endif |
| 2162 | { "th1-setup", 0, 40, 0, "" }, |
| 2163 | { "th1-uri-regexp",0, 40, 0, "" }, |
| 2164 | { "web-browser", 0, 32, 0, "" }, |
| 2165 | { "white-foreground", 0, 0, 0, "off" }, |
| 2166 | { 0,0,0,0,0 } |
| 2167 | }; |
| 2168 | |
| @@ -2354,10 +2355,14 @@ | |
| 2355 | ** is empty and no extra setup is performed. |
| 2356 | ** |
| 2357 | ** th1-setup This is the setup script to be evaluated after creating |
| 2358 | ** and initializing the TH1 interpreter. By default, this |
| 2359 | ** is empty and no extra setup is performed. |
| 2360 | ** |
| 2361 | ** th1-uri-regexp Specify which URI's are allowed in HTTP requests from |
| 2362 | ** TH1 scripts. If empty, no HTTP requests are allowed |
| 2363 | ** whatsoever. The default is an empty string. |
| 2364 | ** |
| 2365 | ** web-browser A shell command used to launch your preferred |
| 2366 | ** web browser when given a URL as an argument. |
| 2367 | ** Defaults to "start" on windows, "open" on Mac, |
| 2368 | ** and "firefox" on Unix. |
| 2369 |
M
src/db.c
+6
-1
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1418,11 +1418,11 @@ | ||
| 1418 | 1418 | blob_appendf(&manifest, "U %F\n", g.zLogin); |
| 1419 | 1419 | md5sum_blob(&manifest, &hash); |
| 1420 | 1420 | blob_appendf(&manifest, "Z %b\n", &hash); |
| 1421 | 1421 | blob_reset(&hash); |
| 1422 | 1422 | rid = content_put(&manifest); |
| 1423 | - manifest_crosslink(rid, &manifest); | |
| 1423 | + manifest_crosslink(rid, &manifest, MC_NONE); | |
| 1424 | 1424 | } |
| 1425 | 1425 | } |
| 1426 | 1426 | |
| 1427 | 1427 | /* |
| 1428 | 1428 | ** COMMAND: new* |
| @@ -2158,10 +2158,11 @@ | ||
| 2158 | 2158 | #ifdef FOSSIL_ENABLE_TCL |
| 2159 | 2159 | { "tcl", 0, 0, 0, "off" }, |
| 2160 | 2160 | { "tcl-setup", 0, 40, 0, "" }, |
| 2161 | 2161 | #endif |
| 2162 | 2162 | { "th1-setup", 0, 40, 0, "" }, |
| 2163 | + { "th1-uri-regexp",0, 40, 0, "" }, | |
| 2163 | 2164 | { "web-browser", 0, 32, 0, "" }, |
| 2164 | 2165 | { "white-foreground", 0, 0, 0, "off" }, |
| 2165 | 2166 | { 0,0,0,0,0 } |
| 2166 | 2167 | }; |
| 2167 | 2168 | |
| @@ -2354,10 +2355,14 @@ | ||
| 2354 | 2355 | ** is empty and no extra setup is performed. |
| 2355 | 2356 | ** |
| 2356 | 2357 | ** th1-setup This is the setup script to be evaluated after creating |
| 2357 | 2358 | ** and initializing the TH1 interpreter. By default, this |
| 2358 | 2359 | ** is empty and no extra setup is performed. |
| 2360 | +** | |
| 2361 | +** th1-uri-regexp Specify which URI's are allowed in HTTP requests from | |
| 2362 | +** TH1 scripts. If empty, no HTTP requests are allowed | |
| 2363 | +** whatsoever. The default is an empty string. | |
| 2359 | 2364 | ** |
| 2360 | 2365 | ** web-browser A shell command used to launch your preferred |
| 2361 | 2366 | ** web browser when given a URL as an argument. |
| 2362 | 2367 | ** Defaults to "start" on windows, "open" on Mac, |
| 2363 | 2368 | ** and "firefox" on Unix. |
| 2364 | 2369 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1418,11 +1418,11 @@ | |
| 1418 | blob_appendf(&manifest, "U %F\n", g.zLogin); |
| 1419 | md5sum_blob(&manifest, &hash); |
| 1420 | blob_appendf(&manifest, "Z %b\n", &hash); |
| 1421 | blob_reset(&hash); |
| 1422 | rid = content_put(&manifest); |
| 1423 | manifest_crosslink(rid, &manifest); |
| 1424 | } |
| 1425 | } |
| 1426 | |
| 1427 | /* |
| 1428 | ** COMMAND: new* |
| @@ -2158,10 +2158,11 @@ | |
| 2158 | #ifdef FOSSIL_ENABLE_TCL |
| 2159 | { "tcl", 0, 0, 0, "off" }, |
| 2160 | { "tcl-setup", 0, 40, 0, "" }, |
| 2161 | #endif |
| 2162 | { "th1-setup", 0, 40, 0, "" }, |
| 2163 | { "web-browser", 0, 32, 0, "" }, |
| 2164 | { "white-foreground", 0, 0, 0, "off" }, |
| 2165 | { 0,0,0,0,0 } |
| 2166 | }; |
| 2167 | |
| @@ -2354,10 +2355,14 @@ | |
| 2354 | ** is empty and no extra setup is performed. |
| 2355 | ** |
| 2356 | ** th1-setup This is the setup script to be evaluated after creating |
| 2357 | ** and initializing the TH1 interpreter. By default, this |
| 2358 | ** is empty and no extra setup is performed. |
| 2359 | ** |
| 2360 | ** web-browser A shell command used to launch your preferred |
| 2361 | ** web browser when given a URL as an argument. |
| 2362 | ** Defaults to "start" on windows, "open" on Mac, |
| 2363 | ** and "firefox" on Unix. |
| 2364 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1418,11 +1418,11 @@ | |
| 1418 | blob_appendf(&manifest, "U %F\n", g.zLogin); |
| 1419 | md5sum_blob(&manifest, &hash); |
| 1420 | blob_appendf(&manifest, "Z %b\n", &hash); |
| 1421 | blob_reset(&hash); |
| 1422 | rid = content_put(&manifest); |
| 1423 | manifest_crosslink(rid, &manifest, MC_NONE); |
| 1424 | } |
| 1425 | } |
| 1426 | |
| 1427 | /* |
| 1428 | ** COMMAND: new* |
| @@ -2158,10 +2158,11 @@ | |
| 2158 | #ifdef FOSSIL_ENABLE_TCL |
| 2159 | { "tcl", 0, 0, 0, "off" }, |
| 2160 | { "tcl-setup", 0, 40, 0, "" }, |
| 2161 | #endif |
| 2162 | { "th1-setup", 0, 40, 0, "" }, |
| 2163 | { "th1-uri-regexp",0, 40, 0, "" }, |
| 2164 | { "web-browser", 0, 32, 0, "" }, |
| 2165 | { "white-foreground", 0, 0, 0, "off" }, |
| 2166 | { 0,0,0,0,0 } |
| 2167 | }; |
| 2168 | |
| @@ -2354,10 +2355,14 @@ | |
| 2355 | ** is empty and no extra setup is performed. |
| 2356 | ** |
| 2357 | ** th1-setup This is the setup script to be evaluated after creating |
| 2358 | ** and initializing the TH1 interpreter. By default, this |
| 2359 | ** is empty and no extra setup is performed. |
| 2360 | ** |
| 2361 | ** th1-uri-regexp Specify which URI's are allowed in HTTP requests from |
| 2362 | ** TH1 scripts. If empty, no HTTP requests are allowed |
| 2363 | ** whatsoever. The default is an empty string. |
| 2364 | ** |
| 2365 | ** web-browser A shell command used to launch your preferred |
| 2366 | ** web browser when given a URL as an argument. |
| 2367 | ** Defaults to "start" on windows, "open" on Mac, |
| 2368 | ** and "firefox" on Unix. |
| 2369 |
+5
-5
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -2179,26 +2179,26 @@ | ||
| 2179 | 2179 | url_add_parameter(&url, "limit", sqlite3_mprintf("%d", iLimit)); |
| 2180 | 2180 | } |
| 2181 | 2181 | url_add_parameter(&url, "log", showLog ? "1" : "0"); |
| 2182 | 2182 | if( showLog ){ |
| 2183 | 2183 | style_submenu_element("Hide Log", "Hide Log", |
| 2184 | - url_render(&url, "log", "0", 0, 0)); | |
| 2184 | + "%s", url_render(&url, "log", "0", 0, 0)); | |
| 2185 | 2185 | }else{ |
| 2186 | 2186 | style_submenu_element("Show Log", "Show Log", |
| 2187 | - url_render(&url, "log", "1", 0, 0)); | |
| 2187 | + "%s", url_render(&url, "log", "1", 0, 0)); | |
| 2188 | 2188 | } |
| 2189 | 2189 | if( ann.bLimit ){ |
| 2190 | 2190 | char *z1, *z2; |
| 2191 | 2191 | style_submenu_element("All Ancestors", "All Ancestors", |
| 2192 | - url_render(&url, "limit", "-1", 0, 0)); | |
| 2192 | + "%s", url_render(&url, "limit", "-1", 0, 0)); | |
| 2193 | 2193 | z1 = sqlite3_mprintf("%d Ancestors", iLimit+20); |
| 2194 | 2194 | z2 = sqlite3_mprintf("%d", iLimit+20); |
| 2195 | - style_submenu_element(z1, z1, url_render(&url, "limit", z2, 0, 0)); | |
| 2195 | + style_submenu_element(z1, z1, "%s", url_render(&url, "limit", z2, 0, 0)); | |
| 2196 | 2196 | } |
| 2197 | 2197 | if( iLimit>20 ){ |
| 2198 | 2198 | style_submenu_element("20 Ancestors", "20 Ancestors", |
| 2199 | - url_render(&url, "limit", "20", 0, 0)); | |
| 2199 | + "%s", url_render(&url, "limit", "20", 0, 0)); | |
| 2200 | 2200 | } |
| 2201 | 2201 | if( db_get_boolean("white-foreground", 0) ){ |
| 2202 | 2202 | clr1 = 0xa04040; |
| 2203 | 2203 | clr2 = 0x4059a0; |
| 2204 | 2204 | }else{ |
| 2205 | 2205 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -2179,26 +2179,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 | |
| @@ -2179,26 +2179,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 | "%s", url_render(&url, "log", "0", 0, 0)); |
| 2185 | }else{ |
| 2186 | style_submenu_element("Show Log", "Show Log", |
| 2187 | "%s", 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 | "%s", 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, "%s", url_render(&url, "limit", z2, 0, 0)); |
| 2196 | } |
| 2197 | if( iLimit>20 ){ |
| 2198 | style_submenu_element("20 Ancestors", "20 Ancestors", |
| 2199 | "%s", 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 |
+1
-1
| --- src/event.c | ||
| +++ src/event.c | ||
| @@ -351,11 +351,11 @@ | ||
| 351 | 351 | md5sum_blob(&event, &cksum); |
| 352 | 352 | blob_appendf(&event, "Z %b\n", &cksum); |
| 353 | 353 | blob_reset(&cksum); |
| 354 | 354 | nrid = content_put(&event); |
| 355 | 355 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 356 | - manifest_crosslink(nrid, &event); | |
| 356 | + manifest_crosslink(nrid, &event, MC_NONE); | |
| 357 | 357 | assert( blob_is_reset(&event) ); |
| 358 | 358 | content_deltify(rid, nrid, 0); |
| 359 | 359 | db_end_transaction(0); |
| 360 | 360 | cgi_redirectf("event?name=%T", zEventId); |
| 361 | 361 | } |
| 362 | 362 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -351,11 +351,11 @@ | |
| 351 | md5sum_blob(&event, &cksum); |
| 352 | blob_appendf(&event, "Z %b\n", &cksum); |
| 353 | blob_reset(&cksum); |
| 354 | nrid = content_put(&event); |
| 355 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 356 | manifest_crosslink(nrid, &event); |
| 357 | assert( blob_is_reset(&event) ); |
| 358 | content_deltify(rid, nrid, 0); |
| 359 | db_end_transaction(0); |
| 360 | cgi_redirectf("event?name=%T", zEventId); |
| 361 | } |
| 362 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -351,11 +351,11 @@ | |
| 351 | md5sum_blob(&event, &cksum); |
| 352 | blob_appendf(&event, "Z %b\n", &cksum); |
| 353 | blob_reset(&cksum); |
| 354 | nrid = content_put(&event); |
| 355 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 356 | manifest_crosslink(nrid, &event, MC_NONE); |
| 357 | assert( blob_is_reset(&event) ); |
| 358 | content_deltify(rid, nrid, 0); |
| 359 | db_end_transaction(0); |
| 360 | cgi_redirectf("event?name=%T", zEventId); |
| 361 | } |
| 362 |
+2
-2
| --- src/finfo.c | ||
| +++ src/finfo.c | ||
| @@ -294,11 +294,11 @@ | ||
| 294 | 294 | url_initialize(&url, "finfo"); |
| 295 | 295 | if( brBg ) url_add_parameter(&url, "brbg", 0); |
| 296 | 296 | if( uBg ) url_add_parameter(&url, "ubg", 0); |
| 297 | 297 | baseCheckin = name_to_rid_www("ci"); |
| 298 | 298 | if( baseCheckin ) firstChngOnly = 1; |
| 299 | - if( firstChngOnly ) url_add_parameter(&url, "fco", "0"); | |
| 299 | + if( !firstChngOnly ) url_add_parameter(&url, "fco", "0"); | |
| 300 | 300 | |
| 301 | 301 | zPrevDate[0] = 0; |
| 302 | 302 | zFilename = PD("name",""); |
| 303 | 303 | url_add_parameter(&url, "name", zFilename); |
| 304 | 304 | blob_zero(&sql); |
| @@ -362,11 +362,11 @@ | ||
| 362 | 362 | style_submenu_element("Full", "Show all changes","%s", |
| 363 | 363 | url_render(&url, "fco", "0", 0, 0)); |
| 364 | 364 | }else{ |
| 365 | 365 | style_submenu_element("Simplified", |
| 366 | 366 | "Show only first use of a change","%s", |
| 367 | - url_render(&url, "fco", "1", 0, 0)); | |
| 367 | + url_render(&url, "fco", 0, 0, 0)); | |
| 368 | 368 | } |
| 369 | 369 | } |
| 370 | 370 | db_prepare(&q, blob_str(&sql)); |
| 371 | 371 | if( P("showsql")!=0 ){ |
| 372 | 372 | @ <p>SQL: %h(blob_str(&sql))</p> |
| 373 | 373 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -294,11 +294,11 @@ | |
| 294 | url_initialize(&url, "finfo"); |
| 295 | if( brBg ) url_add_parameter(&url, "brbg", 0); |
| 296 | if( uBg ) url_add_parameter(&url, "ubg", 0); |
| 297 | baseCheckin = name_to_rid_www("ci"); |
| 298 | if( baseCheckin ) firstChngOnly = 1; |
| 299 | if( firstChngOnly ) url_add_parameter(&url, "fco", "0"); |
| 300 | |
| 301 | zPrevDate[0] = 0; |
| 302 | zFilename = PD("name",""); |
| 303 | url_add_parameter(&url, "name", zFilename); |
| 304 | blob_zero(&sql); |
| @@ -362,11 +362,11 @@ | |
| 362 | style_submenu_element("Full", "Show all changes","%s", |
| 363 | url_render(&url, "fco", "0", 0, 0)); |
| 364 | }else{ |
| 365 | style_submenu_element("Simplified", |
| 366 | "Show only first use of a change","%s", |
| 367 | url_render(&url, "fco", "1", 0, 0)); |
| 368 | } |
| 369 | } |
| 370 | db_prepare(&q, blob_str(&sql)); |
| 371 | if( P("showsql")!=0 ){ |
| 372 | @ <p>SQL: %h(blob_str(&sql))</p> |
| 373 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -294,11 +294,11 @@ | |
| 294 | url_initialize(&url, "finfo"); |
| 295 | if( brBg ) url_add_parameter(&url, "brbg", 0); |
| 296 | if( uBg ) url_add_parameter(&url, "ubg", 0); |
| 297 | baseCheckin = name_to_rid_www("ci"); |
| 298 | if( baseCheckin ) firstChngOnly = 1; |
| 299 | if( !firstChngOnly ) url_add_parameter(&url, "fco", "0"); |
| 300 | |
| 301 | zPrevDate[0] = 0; |
| 302 | zFilename = PD("name",""); |
| 303 | url_add_parameter(&url, "name", zFilename); |
| 304 | blob_zero(&sql); |
| @@ -362,11 +362,11 @@ | |
| 362 | style_submenu_element("Full", "Show all changes","%s", |
| 363 | url_render(&url, "fco", "0", 0, 0)); |
| 364 | }else{ |
| 365 | style_submenu_element("Simplified", |
| 366 | "Show only first use of a change","%s", |
| 367 | url_render(&url, "fco", 0, 0, 0)); |
| 368 | } |
| 369 | } |
| 370 | db_prepare(&q, blob_str(&sql)); |
| 371 | if( P("showsql")!=0 ){ |
| 372 | @ <p>SQL: %h(blob_str(&sql))</p> |
| 373 |
+2
-2
| --- src/gzip.c | ||
| +++ src/gzip.c | ||
| @@ -55,11 +55,11 @@ | ||
| 55 | 55 | blob_zero(&gzip.out); |
| 56 | 56 | aHdr[0] = 0x1f; |
| 57 | 57 | aHdr[1] = 0x8b; |
| 58 | 58 | aHdr[2] = 8; |
| 59 | 59 | aHdr[3] = 0; |
| 60 | - if( now==0 ){ | |
| 60 | + if( now==-1 ){ | |
| 61 | 61 | now = db_int64(0, "SELECT (julianday('now') - 2440587.5)*86400.0"); |
| 62 | 62 | } |
| 63 | 63 | put32(&aHdr[4], now&0xffffffff); |
| 64 | 64 | aHdr[8] = 2; |
| 65 | 65 | aHdr[9] = 255; |
| @@ -126,15 +126,15 @@ | ||
| 126 | 126 | void test_gzip_cmd(void){ |
| 127 | 127 | Blob b; |
| 128 | 128 | char *zOut; |
| 129 | 129 | if( g.argc!=3 ) usage("FILENAME"); |
| 130 | 130 | sqlite3_open(":memory:", &g.db); |
| 131 | - gzip_begin(0); | |
| 131 | + gzip_begin(-1); | |
| 132 | 132 | blob_read_from_file(&b, g.argv[2]); |
| 133 | 133 | zOut = mprintf("%s.gz", g.argv[2]); |
| 134 | 134 | gzip_step(blob_buffer(&b), blob_size(&b)); |
| 135 | 135 | blob_reset(&b); |
| 136 | 136 | gzip_finish(&b); |
| 137 | 137 | blob_write_to_file(&b, zOut); |
| 138 | 138 | blob_reset(&b); |
| 139 | 139 | fossil_free(zOut); |
| 140 | 140 | } |
| 141 | 141 |
| --- src/gzip.c | |
| +++ src/gzip.c | |
| @@ -55,11 +55,11 @@ | |
| 55 | blob_zero(&gzip.out); |
| 56 | aHdr[0] = 0x1f; |
| 57 | aHdr[1] = 0x8b; |
| 58 | aHdr[2] = 8; |
| 59 | aHdr[3] = 0; |
| 60 | if( now==0 ){ |
| 61 | now = db_int64(0, "SELECT (julianday('now') - 2440587.5)*86400.0"); |
| 62 | } |
| 63 | put32(&aHdr[4], now&0xffffffff); |
| 64 | aHdr[8] = 2; |
| 65 | aHdr[9] = 255; |
| @@ -126,15 +126,15 @@ | |
| 126 | void test_gzip_cmd(void){ |
| 127 | Blob b; |
| 128 | char *zOut; |
| 129 | if( g.argc!=3 ) usage("FILENAME"); |
| 130 | sqlite3_open(":memory:", &g.db); |
| 131 | gzip_begin(0); |
| 132 | blob_read_from_file(&b, g.argv[2]); |
| 133 | zOut = mprintf("%s.gz", g.argv[2]); |
| 134 | gzip_step(blob_buffer(&b), blob_size(&b)); |
| 135 | blob_reset(&b); |
| 136 | gzip_finish(&b); |
| 137 | blob_write_to_file(&b, zOut); |
| 138 | blob_reset(&b); |
| 139 | fossil_free(zOut); |
| 140 | } |
| 141 |
| --- src/gzip.c | |
| +++ src/gzip.c | |
| @@ -55,11 +55,11 @@ | |
| 55 | blob_zero(&gzip.out); |
| 56 | aHdr[0] = 0x1f; |
| 57 | aHdr[1] = 0x8b; |
| 58 | aHdr[2] = 8; |
| 59 | aHdr[3] = 0; |
| 60 | if( now==-1 ){ |
| 61 | now = db_int64(0, "SELECT (julianday('now') - 2440587.5)*86400.0"); |
| 62 | } |
| 63 | put32(&aHdr[4], now&0xffffffff); |
| 64 | aHdr[8] = 2; |
| 65 | aHdr[9] = 255; |
| @@ -126,15 +126,15 @@ | |
| 126 | void test_gzip_cmd(void){ |
| 127 | Blob b; |
| 128 | char *zOut; |
| 129 | if( g.argc!=3 ) usage("FILENAME"); |
| 130 | sqlite3_open(":memory:", &g.db); |
| 131 | gzip_begin(-1); |
| 132 | blob_read_from_file(&b, g.argv[2]); |
| 133 | zOut = mprintf("%s.gz", g.argv[2]); |
| 134 | gzip_step(blob_buffer(&b), blob_size(&b)); |
| 135 | blob_reset(&b); |
| 136 | gzip_finish(&b); |
| 137 | blob_write_to_file(&b, zOut); |
| 138 | blob_reset(&b); |
| 139 | fossil_free(zOut); |
| 140 | } |
| 141 |
+12
-13
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -110,12 +110,11 @@ | ||
| 110 | 110 | blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded); |
| 111 | 111 | fossil_free(zEncoded); |
| 112 | 112 | fossil_free(zCredentials); |
| 113 | 113 | } |
| 114 | 114 | blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); |
| 115 | - blob_appendf(pHdr, "User-Agent: Fossil/" RELEASE_VERSION | |
| 116 | - " (" MANIFEST_DATE " " MANIFEST_VERSION ")\r\n"); | |
| 115 | + blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent()); | |
| 117 | 116 | if( g.urlIsSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n"); |
| 118 | 117 | if( g.fHttpTrace ){ |
| 119 | 118 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| 120 | 119 | }else{ |
| 121 | 120 | blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); |
| @@ -144,12 +143,12 @@ | ||
| 144 | 143 | char *zLine; /* A single line of the reply header */ |
| 145 | 144 | int i; /* Loop counter */ |
| 146 | 145 | int isError = 0; /* True if the reply is an error message */ |
| 147 | 146 | int isCompressed = 1; /* True if the reply is compressed */ |
| 148 | 147 | |
| 149 | - if( transport_open() ){ | |
| 150 | - fossil_warning(transport_errmsg()); | |
| 148 | + if( transport_open(GLOBAL_URL()) ){ | |
| 149 | + fossil_warning(transport_errmsg(GLOBAL_URL())); | |
| 151 | 150 | return 1; |
| 152 | 151 | } |
| 153 | 152 | |
| 154 | 153 | /* Construct the login card and prepare the complete payload */ |
| 155 | 154 | blob_zero(&login); |
| @@ -191,22 +190,22 @@ | ||
| 191 | 190 | } |
| 192 | 191 | |
| 193 | 192 | /* |
| 194 | 193 | ** Send the request to the server. |
| 195 | 194 | */ |
| 196 | - transport_send(&hdr); | |
| 197 | - transport_send(&payload); | |
| 195 | + transport_send(GLOBAL_URL(), &hdr); | |
| 196 | + transport_send(GLOBAL_URL(), &payload); | |
| 198 | 197 | blob_reset(&hdr); |
| 199 | 198 | blob_reset(&payload); |
| 200 | - transport_flip(); | |
| 199 | + transport_flip(GLOBAL_URL()); | |
| 201 | 200 | |
| 202 | 201 | /* |
| 203 | 202 | ** Read and interpret the server reply |
| 204 | 203 | */ |
| 205 | 204 | closeConnection = 1; |
| 206 | 205 | iLength = -1; |
| 207 | - while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){ | |
| 206 | + while( (zLine = transport_receive_line(GLOBAL_URL()))!=0 && zLine[0]!=0 ){ | |
| 208 | 207 | /* printf("[%s]\n", zLine); fflush(stdout); */ |
| 209 | 208 | if( fossil_strnicmp(zLine, "http/1.", 7)==0 ){ |
| 210 | 209 | if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; |
| 211 | 210 | if( rc!=200 && rc!=302 ){ |
| 212 | 211 | int ii; |
| @@ -255,11 +254,11 @@ | ||
| 255 | 254 | j -= 4; |
| 256 | 255 | zLine[j] = 0; |
| 257 | 256 | } |
| 258 | 257 | fossil_print("redirect to %s\n", &zLine[i]); |
| 259 | 258 | url_parse(&zLine[i], 0); |
| 260 | - transport_close(); | |
| 259 | + transport_close(GLOBAL_URL()); | |
| 261 | 260 | return http_exchange(pSend, pReply, useLogin, maxRedirect); |
| 262 | 261 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 263 | 262 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 264 | 263 | isCompressed = 0; |
| 265 | 264 | }else if( fossil_strnicmp(&zLine[14], |
| @@ -282,11 +281,11 @@ | ||
| 282 | 281 | /* |
| 283 | 282 | ** Extract the reply payload that follows the header |
| 284 | 283 | */ |
| 285 | 284 | blob_zero(pReply); |
| 286 | 285 | blob_resize(pReply, iLength); |
| 287 | - iLength = transport_receive(blob_buffer(pReply), iLength); | |
| 286 | + iLength = transport_receive(GLOBAL_URL(), blob_buffer(pReply), iLength); | |
| 288 | 287 | blob_resize(pReply, iLength); |
| 289 | 288 | if( isError ){ |
| 290 | 289 | char *z; |
| 291 | 290 | int i, j; |
| 292 | 291 | z = blob_str(pReply); |
| @@ -311,18 +310,18 @@ | ||
| 311 | 310 | ** |
| 312 | 311 | ** For SSH we will leave the connection open. |
| 313 | 312 | */ |
| 314 | 313 | if( ! g.urlIsSsh ) closeConnection = 1; /* FIX ME */ |
| 315 | 314 | if( closeConnection ){ |
| 316 | - transport_close(); | |
| 315 | + transport_close(GLOBAL_URL()); | |
| 317 | 316 | }else{ |
| 318 | - transport_rewind(); | |
| 317 | + transport_rewind(GLOBAL_URL()); | |
| 319 | 318 | } |
| 320 | 319 | return 0; |
| 321 | 320 | |
| 322 | 321 | /* |
| 323 | 322 | ** Jump to here if an error is seen. |
| 324 | 323 | */ |
| 325 | 324 | write_err: |
| 326 | - transport_close(); | |
| 325 | + transport_close(GLOBAL_URL()); | |
| 327 | 326 | return 1; |
| 328 | 327 | } |
| 329 | 328 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -110,12 +110,11 @@ | |
| 110 | blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded); |
| 111 | fossil_free(zEncoded); |
| 112 | fossil_free(zCredentials); |
| 113 | } |
| 114 | blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); |
| 115 | blob_appendf(pHdr, "User-Agent: Fossil/" RELEASE_VERSION |
| 116 | " (" MANIFEST_DATE " " MANIFEST_VERSION ")\r\n"); |
| 117 | if( g.urlIsSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n"); |
| 118 | if( g.fHttpTrace ){ |
| 119 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| 120 | }else{ |
| 121 | blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); |
| @@ -144,12 +143,12 @@ | |
| 144 | char *zLine; /* A single line of the reply header */ |
| 145 | int i; /* Loop counter */ |
| 146 | int isError = 0; /* True if the reply is an error message */ |
| 147 | int isCompressed = 1; /* True if the reply is compressed */ |
| 148 | |
| 149 | if( transport_open() ){ |
| 150 | fossil_warning(transport_errmsg()); |
| 151 | return 1; |
| 152 | } |
| 153 | |
| 154 | /* Construct the login card and prepare the complete payload */ |
| 155 | blob_zero(&login); |
| @@ -191,22 +190,22 @@ | |
| 191 | } |
| 192 | |
| 193 | /* |
| 194 | ** Send the request to the server. |
| 195 | */ |
| 196 | transport_send(&hdr); |
| 197 | transport_send(&payload); |
| 198 | blob_reset(&hdr); |
| 199 | blob_reset(&payload); |
| 200 | transport_flip(); |
| 201 | |
| 202 | /* |
| 203 | ** Read and interpret the server reply |
| 204 | */ |
| 205 | closeConnection = 1; |
| 206 | iLength = -1; |
| 207 | while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){ |
| 208 | /* printf("[%s]\n", zLine); fflush(stdout); */ |
| 209 | if( fossil_strnicmp(zLine, "http/1.", 7)==0 ){ |
| 210 | if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; |
| 211 | if( rc!=200 && rc!=302 ){ |
| 212 | int ii; |
| @@ -255,11 +254,11 @@ | |
| 255 | j -= 4; |
| 256 | zLine[j] = 0; |
| 257 | } |
| 258 | fossil_print("redirect to %s\n", &zLine[i]); |
| 259 | url_parse(&zLine[i], 0); |
| 260 | transport_close(); |
| 261 | return http_exchange(pSend, pReply, useLogin, maxRedirect); |
| 262 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 263 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 264 | isCompressed = 0; |
| 265 | }else if( fossil_strnicmp(&zLine[14], |
| @@ -282,11 +281,11 @@ | |
| 282 | /* |
| 283 | ** Extract the reply payload that follows the header |
| 284 | */ |
| 285 | blob_zero(pReply); |
| 286 | blob_resize(pReply, iLength); |
| 287 | iLength = transport_receive(blob_buffer(pReply), iLength); |
| 288 | blob_resize(pReply, iLength); |
| 289 | if( isError ){ |
| 290 | char *z; |
| 291 | int i, j; |
| 292 | z = blob_str(pReply); |
| @@ -311,18 +310,18 @@ | |
| 311 | ** |
| 312 | ** For SSH we will leave the connection open. |
| 313 | */ |
| 314 | if( ! g.urlIsSsh ) closeConnection = 1; /* FIX ME */ |
| 315 | if( closeConnection ){ |
| 316 | transport_close(); |
| 317 | }else{ |
| 318 | transport_rewind(); |
| 319 | } |
| 320 | return 0; |
| 321 | |
| 322 | /* |
| 323 | ** Jump to here if an error is seen. |
| 324 | */ |
| 325 | write_err: |
| 326 | transport_close(); |
| 327 | return 1; |
| 328 | } |
| 329 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -110,12 +110,11 @@ | |
| 110 | blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded); |
| 111 | fossil_free(zEncoded); |
| 112 | fossil_free(zCredentials); |
| 113 | } |
| 114 | blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); |
| 115 | blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent()); |
| 116 | if( g.urlIsSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n"); |
| 117 | if( g.fHttpTrace ){ |
| 118 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| 119 | }else{ |
| 120 | blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); |
| @@ -144,12 +143,12 @@ | |
| 143 | char *zLine; /* A single line of the reply header */ |
| 144 | int i; /* Loop counter */ |
| 145 | int isError = 0; /* True if the reply is an error message */ |
| 146 | int isCompressed = 1; /* True if the reply is compressed */ |
| 147 | |
| 148 | if( transport_open(GLOBAL_URL()) ){ |
| 149 | fossil_warning(transport_errmsg(GLOBAL_URL())); |
| 150 | return 1; |
| 151 | } |
| 152 | |
| 153 | /* Construct the login card and prepare the complete payload */ |
| 154 | blob_zero(&login); |
| @@ -191,22 +190,22 @@ | |
| 190 | } |
| 191 | |
| 192 | /* |
| 193 | ** Send the request to the server. |
| 194 | */ |
| 195 | transport_send(GLOBAL_URL(), &hdr); |
| 196 | transport_send(GLOBAL_URL(), &payload); |
| 197 | blob_reset(&hdr); |
| 198 | blob_reset(&payload); |
| 199 | transport_flip(GLOBAL_URL()); |
| 200 | |
| 201 | /* |
| 202 | ** Read and interpret the server reply |
| 203 | */ |
| 204 | closeConnection = 1; |
| 205 | iLength = -1; |
| 206 | while( (zLine = transport_receive_line(GLOBAL_URL()))!=0 && zLine[0]!=0 ){ |
| 207 | /* printf("[%s]\n", zLine); fflush(stdout); */ |
| 208 | if( fossil_strnicmp(zLine, "http/1.", 7)==0 ){ |
| 209 | if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; |
| 210 | if( rc!=200 && rc!=302 ){ |
| 211 | int ii; |
| @@ -255,11 +254,11 @@ | |
| 254 | j -= 4; |
| 255 | zLine[j] = 0; |
| 256 | } |
| 257 | fossil_print("redirect to %s\n", &zLine[i]); |
| 258 | url_parse(&zLine[i], 0); |
| 259 | transport_close(GLOBAL_URL()); |
| 260 | return http_exchange(pSend, pReply, useLogin, maxRedirect); |
| 261 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 262 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 263 | isCompressed = 0; |
| 264 | }else if( fossil_strnicmp(&zLine[14], |
| @@ -282,11 +281,11 @@ | |
| 281 | /* |
| 282 | ** Extract the reply payload that follows the header |
| 283 | */ |
| 284 | blob_zero(pReply); |
| 285 | blob_resize(pReply, iLength); |
| 286 | iLength = transport_receive(GLOBAL_URL(), blob_buffer(pReply), iLength); |
| 287 | blob_resize(pReply, iLength); |
| 288 | if( isError ){ |
| 289 | char *z; |
| 290 | int i, j; |
| 291 | z = blob_str(pReply); |
| @@ -311,18 +310,18 @@ | |
| 310 | ** |
| 311 | ** For SSH we will leave the connection open. |
| 312 | */ |
| 313 | if( ! g.urlIsSsh ) closeConnection = 1; /* FIX ME */ |
| 314 | if( closeConnection ){ |
| 315 | transport_close(GLOBAL_URL()); |
| 316 | }else{ |
| 317 | transport_rewind(GLOBAL_URL()); |
| 318 | } |
| 319 | return 0; |
| 320 | |
| 321 | /* |
| 322 | ** Jump to here if an error is seen. |
| 323 | */ |
| 324 | write_err: |
| 325 | transport_close(GLOBAL_URL()); |
| 326 | return 1; |
| 327 | } |
| 328 |
+9
-8
| --- src/http_socket.c | ||
| +++ src/http_socket.c | ||
| @@ -131,29 +131,29 @@ | ||
| 131 | 131 | ** g.urlName Name of the server. Ex: www.fossil-scm.org |
| 132 | 132 | ** g.urlPort TCP/IP port to use. Ex: 80 |
| 133 | 133 | ** |
| 134 | 134 | ** Return the number of errors. |
| 135 | 135 | */ |
| 136 | -int socket_open(void){ | |
| 136 | +int socket_open(UrlData *pUrlData){ | |
| 137 | 137 | static struct sockaddr_in addr; /* The server address */ |
| 138 | 138 | static int addrIsInit = 0; /* True once addr is initialized */ |
| 139 | 139 | |
| 140 | 140 | socket_global_init(); |
| 141 | 141 | if( !addrIsInit ){ |
| 142 | 142 | addr.sin_family = AF_INET; |
| 143 | - addr.sin_port = htons(g.urlPort); | |
| 144 | - *(int*)&addr.sin_addr = inet_addr(g.urlName); | |
| 143 | + addr.sin_port = htons(pUrlData->port); | |
| 144 | + *(int*)&addr.sin_addr = inet_addr(pUrlData->name); | |
| 145 | 145 | if( -1 == *(int*)&addr.sin_addr ){ |
| 146 | 146 | #ifndef FOSSIL_STATIC_LINK |
| 147 | 147 | struct hostent *pHost; |
| 148 | - pHost = gethostbyname(g.urlName); | |
| 148 | + pHost = gethostbyname(pUrlData->name); | |
| 149 | 149 | if( pHost!=0 ){ |
| 150 | 150 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 151 | 151 | }else |
| 152 | 152 | #endif |
| 153 | 153 | { |
| 154 | - socket_set_errmsg("can't resolve host name: %s", g.urlName); | |
| 154 | + socket_set_errmsg("can't resolve host name: %s", pUrlData->name); | |
| 155 | 155 | return 1; |
| 156 | 156 | } |
| 157 | 157 | } |
| 158 | 158 | addrIsInit = 1; |
| 159 | 159 | |
| @@ -167,11 +167,12 @@ | ||
| 167 | 167 | if( iSocket<0 ){ |
| 168 | 168 | socket_set_errmsg("cannot create a socket"); |
| 169 | 169 | return 1; |
| 170 | 170 | } |
| 171 | 171 | if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){ |
| 172 | - socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort); | |
| 172 | + socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name, | |
| 173 | + pUrlData->port); | |
| 173 | 174 | socket_close(); |
| 174 | 175 | return 1; |
| 175 | 176 | } |
| 176 | 177 | #if !defined(_WIN32) |
| 177 | 178 | signal(SIGPIPE, SIG_IGN); |
| @@ -215,16 +216,16 @@ | ||
| 215 | 216 | /* |
| 216 | 217 | ** Attempt to resolve g.urlName to IP and setup g.zIpAddr so rcvfrom gets |
| 217 | 218 | ** populated. For hostnames with more than one IP (or if overridden in |
| 218 | 219 | ** ~/.ssh/config) the rcvfrom may not match the host to which we connect. |
| 219 | 220 | */ |
| 220 | -void socket_ssh_resolve_addr(void){ | |
| 221 | +void socket_ssh_resolve_addr(UrlData *pUrlData){ | |
| 221 | 222 | struct hostent *pHost; /* Used to make best effort for rcvfrom */ |
| 222 | 223 | struct sockaddr_in addr; |
| 223 | 224 | |
| 224 | 225 | memset(&addr, 0, sizeof(addr)); |
| 225 | - pHost = gethostbyname(g.urlName); | |
| 226 | + pHost = gethostbyname(pUrlData->name); | |
| 226 | 227 | if( pHost!=0 ){ |
| 227 | 228 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 228 | 229 | g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr)); |
| 229 | 230 | } |
| 230 | 231 | } |
| 231 | 232 |
| --- src/http_socket.c | |
| +++ src/http_socket.c | |
| @@ -131,29 +131,29 @@ | |
| 131 | ** g.urlName Name of the server. Ex: www.fossil-scm.org |
| 132 | ** g.urlPort TCP/IP port to use. Ex: 80 |
| 133 | ** |
| 134 | ** Return the number of errors. |
| 135 | */ |
| 136 | int socket_open(void){ |
| 137 | static struct sockaddr_in addr; /* The server address */ |
| 138 | static int addrIsInit = 0; /* True once addr is initialized */ |
| 139 | |
| 140 | socket_global_init(); |
| 141 | if( !addrIsInit ){ |
| 142 | addr.sin_family = AF_INET; |
| 143 | addr.sin_port = htons(g.urlPort); |
| 144 | *(int*)&addr.sin_addr = inet_addr(g.urlName); |
| 145 | if( -1 == *(int*)&addr.sin_addr ){ |
| 146 | #ifndef FOSSIL_STATIC_LINK |
| 147 | struct hostent *pHost; |
| 148 | pHost = gethostbyname(g.urlName); |
| 149 | if( pHost!=0 ){ |
| 150 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 151 | }else |
| 152 | #endif |
| 153 | { |
| 154 | socket_set_errmsg("can't resolve host name: %s", g.urlName); |
| 155 | return 1; |
| 156 | } |
| 157 | } |
| 158 | addrIsInit = 1; |
| 159 | |
| @@ -167,11 +167,12 @@ | |
| 167 | if( iSocket<0 ){ |
| 168 | socket_set_errmsg("cannot create a socket"); |
| 169 | return 1; |
| 170 | } |
| 171 | if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){ |
| 172 | socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort); |
| 173 | socket_close(); |
| 174 | return 1; |
| 175 | } |
| 176 | #if !defined(_WIN32) |
| 177 | signal(SIGPIPE, SIG_IGN); |
| @@ -215,16 +216,16 @@ | |
| 215 | /* |
| 216 | ** Attempt to resolve g.urlName to IP and setup g.zIpAddr so rcvfrom gets |
| 217 | ** populated. For hostnames with more than one IP (or if overridden in |
| 218 | ** ~/.ssh/config) the rcvfrom may not match the host to which we connect. |
| 219 | */ |
| 220 | void socket_ssh_resolve_addr(void){ |
| 221 | struct hostent *pHost; /* Used to make best effort for rcvfrom */ |
| 222 | struct sockaddr_in addr; |
| 223 | |
| 224 | memset(&addr, 0, sizeof(addr)); |
| 225 | pHost = gethostbyname(g.urlName); |
| 226 | if( pHost!=0 ){ |
| 227 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 228 | g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr)); |
| 229 | } |
| 230 | } |
| 231 |
| --- src/http_socket.c | |
| +++ src/http_socket.c | |
| @@ -131,29 +131,29 @@ | |
| 131 | ** g.urlName Name of the server. Ex: www.fossil-scm.org |
| 132 | ** g.urlPort TCP/IP port to use. Ex: 80 |
| 133 | ** |
| 134 | ** Return the number of errors. |
| 135 | */ |
| 136 | int socket_open(UrlData *pUrlData){ |
| 137 | static struct sockaddr_in addr; /* The server address */ |
| 138 | static int addrIsInit = 0; /* True once addr is initialized */ |
| 139 | |
| 140 | socket_global_init(); |
| 141 | if( !addrIsInit ){ |
| 142 | addr.sin_family = AF_INET; |
| 143 | addr.sin_port = htons(pUrlData->port); |
| 144 | *(int*)&addr.sin_addr = inet_addr(pUrlData->name); |
| 145 | if( -1 == *(int*)&addr.sin_addr ){ |
| 146 | #ifndef FOSSIL_STATIC_LINK |
| 147 | struct hostent *pHost; |
| 148 | pHost = gethostbyname(pUrlData->name); |
| 149 | if( pHost!=0 ){ |
| 150 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 151 | }else |
| 152 | #endif |
| 153 | { |
| 154 | socket_set_errmsg("can't resolve host name: %s", pUrlData->name); |
| 155 | return 1; |
| 156 | } |
| 157 | } |
| 158 | addrIsInit = 1; |
| 159 | |
| @@ -167,11 +167,12 @@ | |
| 167 | if( iSocket<0 ){ |
| 168 | socket_set_errmsg("cannot create a socket"); |
| 169 | return 1; |
| 170 | } |
| 171 | if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){ |
| 172 | socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name, |
| 173 | pUrlData->port); |
| 174 | socket_close(); |
| 175 | return 1; |
| 176 | } |
| 177 | #if !defined(_WIN32) |
| 178 | signal(SIGPIPE, SIG_IGN); |
| @@ -215,16 +216,16 @@ | |
| 216 | /* |
| 217 | ** Attempt to resolve g.urlName to IP and setup g.zIpAddr so rcvfrom gets |
| 218 | ** populated. For hostnames with more than one IP (or if overridden in |
| 219 | ** ~/.ssh/config) the rcvfrom may not match the host to which we connect. |
| 220 | */ |
| 221 | void socket_ssh_resolve_addr(UrlData *pUrlData){ |
| 222 | struct hostent *pHost; /* Used to make best effort for rcvfrom */ |
| 223 | struct sockaddr_in addr; |
| 224 | |
| 225 | memset(&addr, 0, sizeof(addr)); |
| 226 | pHost = gethostbyname(pUrlData->name); |
| 227 | if( pHost!=0 ){ |
| 228 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 229 | g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr)); |
| 230 | } |
| 231 | } |
| 232 |
+17
-15
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -183,11 +183,11 @@ | ||
| 183 | 183 | ** g.urlName Name of the server. Ex: www.fossil-scm.org |
| 184 | 184 | ** g.urlPort TCP/IP port to use. Ex: 80 |
| 185 | 185 | ** |
| 186 | 186 | ** Return the number of errors. |
| 187 | 187 | */ |
| 188 | -int ssl_open(void){ | |
| 188 | +int ssl_open(UrlData *pUrlData){ | |
| 189 | 189 | X509 *cert; |
| 190 | 190 | int hasSavedCertificate = 0; |
| 191 | 191 | int trusted = 0; |
| 192 | 192 | unsigned long e; |
| 193 | 193 | |
| @@ -194,11 +194,11 @@ | ||
| 194 | 194 | ssl_global_init(); |
| 195 | 195 | |
| 196 | 196 | /* Get certificate for current server from global config and |
| 197 | 197 | * (if we have it in config) add it to certificate store. |
| 198 | 198 | */ |
| 199 | - cert = ssl_get_certificate(&trusted); | |
| 199 | + cert = ssl_get_certificate(pUrlData, &trusted); | |
| 200 | 200 | if ( cert!=NULL ){ |
| 201 | 201 | X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert); |
| 202 | 202 | X509_free(cert); |
| 203 | 203 | hasSavedCertificate = 1; |
| 204 | 204 | } |
| @@ -205,11 +205,11 @@ | ||
| 205 | 205 | |
| 206 | 206 | iBio = BIO_new_ssl_connect(sslCtx); |
| 207 | 207 | BIO_get_ssl(iBio, &ssl); |
| 208 | 208 | |
| 209 | 209 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 210 | - if( !SSL_set_tlsext_host_name(ssl, g.urlName) ){ | |
| 210 | + if( !SSL_set_tlsext_host_name(ssl, pUrlData->name) ){ | |
| 211 | 211 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 212 | 212 | "continuing without it.\n"); |
| 213 | 213 | } |
| 214 | 214 | #endif |
| 215 | 215 | |
| @@ -218,23 +218,25 @@ | ||
| 218 | 218 | ssl_set_errmsg("SSL: cannot open SSL (%s)", |
| 219 | 219 | ERR_reason_error_string(ERR_get_error())); |
| 220 | 220 | return 1; |
| 221 | 221 | } |
| 222 | 222 | |
| 223 | - BIO_set_conn_hostname(iBio, g.urlName); | |
| 224 | - BIO_set_conn_int_port(iBio, &g.urlPort); | |
| 223 | + BIO_set_conn_hostname(iBio, pUrlData->name); | |
| 224 | + BIO_set_conn_int_port(iBio, &pUrlData->port); | |
| 225 | 225 | |
| 226 | 226 | if( BIO_do_connect(iBio)<=0 ){ |
| 227 | 227 | ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", |
| 228 | - g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error())); | |
| 228 | + pUrlData->name, pUrlData->port, | |
| 229 | + ERR_reason_error_string(ERR_get_error())); | |
| 229 | 230 | ssl_close(); |
| 230 | 231 | return 1; |
| 231 | 232 | } |
| 232 | 233 | |
| 233 | 234 | if( BIO_do_handshake(iBio)<=0 ) { |
| 234 | 235 | ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)", |
| 235 | - g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error())); | |
| 236 | + pUrlData->name, pUrlData->port, | |
| 237 | + ERR_reason_error_string(ERR_get_error())); | |
| 236 | 238 | ssl_close(); |
| 237 | 239 | return 1; |
| 238 | 240 | } |
| 239 | 241 | /* Check if certificate is valid */ |
| 240 | 242 | cert = SSL_get_peer_certificate(ssl); |
| @@ -281,11 +283,11 @@ | ||
| 281 | 283 | " certificates list\n\n" |
| 282 | 284 | "If you are not expecting this message, answer no and " |
| 283 | 285 | "contact your server\nadministrator.\n\n" |
| 284 | 286 | "Accept certificate for host %s (a=always/y/N)? ", |
| 285 | 287 | X509_verify_cert_error_string(e), desc, warning, |
| 286 | - g.urlName); | |
| 288 | + pUrlData->name); | |
| 287 | 289 | BIO_free(mem); |
| 288 | 290 | |
| 289 | 291 | prompt_user(prompt, &ans); |
| 290 | 292 | free(prompt); |
| 291 | 293 | cReply = blob_str(&ans)[0]; |
| @@ -302,11 +304,11 @@ | ||
| 302 | 304 | &ans); |
| 303 | 305 | cReply = blob_str(&ans)[0]; |
| 304 | 306 | trusted = ( cReply=='a' || cReply=='A' ); |
| 305 | 307 | blob_reset(&ans); |
| 306 | 308 | } |
| 307 | - ssl_save_certificate(cert, trusted); | |
| 309 | + ssl_save_certificate(pUrlData, cert, trusted); | |
| 308 | 310 | } |
| 309 | 311 | } |
| 310 | 312 | |
| 311 | 313 | /* Set the Global.zIpAddr variable to the server we are talking to. |
| 312 | 314 | ** This is used to populate the ipaddr column of the rcvfrom table, |
| @@ -323,44 +325,44 @@ | ||
| 323 | 325 | } |
| 324 | 326 | |
| 325 | 327 | /* |
| 326 | 328 | ** Save certificate to global config. |
| 327 | 329 | */ |
| 328 | -void ssl_save_certificate(X509 *cert, int trusted){ | |
| 330 | +void ssl_save_certificate(UrlData *pUrlData, X509 *cert, int trusted){ | |
| 329 | 331 | BIO *mem; |
| 330 | 332 | char *zCert, *zHost; |
| 331 | 333 | |
| 332 | 334 | mem = BIO_new(BIO_s_mem()); |
| 333 | 335 | PEM_write_bio_X509(mem, cert); |
| 334 | 336 | BIO_write(mem, "", 1); /* nul-terminate mem buffer */ |
| 335 | 337 | BIO_get_mem_data(mem, &zCert); |
| 336 | - zHost = mprintf("cert:%s", g.urlName); | |
| 338 | + zHost = mprintf("cert:%s", pUrlData->name); | |
| 337 | 339 | db_set(zHost, zCert, 1); |
| 338 | 340 | free(zHost); |
| 339 | - zHost = mprintf("trusted:%s", g.urlName); | |
| 341 | + zHost = mprintf("trusted:%s", pUrlData->name); | |
| 340 | 342 | db_set_int(zHost, trusted, 1); |
| 341 | 343 | free(zHost); |
| 342 | 344 | BIO_free(mem); |
| 343 | 345 | } |
| 344 | 346 | |
| 345 | 347 | /* |
| 346 | 348 | ** Get certificate for g.urlName from global config. |
| 347 | 349 | ** Return NULL if no certificate found. |
| 348 | 350 | */ |
| 349 | -X509 *ssl_get_certificate(int *pTrusted){ | |
| 351 | +X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){ | |
| 350 | 352 | char *zHost, *zCert; |
| 351 | 353 | BIO *mem; |
| 352 | 354 | X509 *cert; |
| 353 | 355 | |
| 354 | - zHost = mprintf("cert:%s", g.urlName); | |
| 356 | + zHost = mprintf("cert:%s", pUrlData->name); | |
| 355 | 357 | zCert = db_get(zHost, NULL); |
| 356 | 358 | free(zHost); |
| 357 | 359 | if ( zCert==NULL ) |
| 358 | 360 | return NULL; |
| 359 | 361 | |
| 360 | 362 | if ( pTrusted!=0 ){ |
| 361 | - zHost = mprintf("trusted:%s", g.urlName); | |
| 363 | + zHost = mprintf("trusted:%s", pUrlData->name); | |
| 362 | 364 | *pTrusted = db_get_int(zHost, 0); |
| 363 | 365 | free(zHost); |
| 364 | 366 | } |
| 365 | 367 | |
| 366 | 368 | mem = BIO_new(BIO_s_mem()); |
| 367 | 369 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -183,11 +183,11 @@ | |
| 183 | ** g.urlName Name of the server. Ex: www.fossil-scm.org |
| 184 | ** g.urlPort TCP/IP port to use. Ex: 80 |
| 185 | ** |
| 186 | ** Return the number of errors. |
| 187 | */ |
| 188 | int ssl_open(void){ |
| 189 | X509 *cert; |
| 190 | int hasSavedCertificate = 0; |
| 191 | int trusted = 0; |
| 192 | unsigned long e; |
| 193 | |
| @@ -194,11 +194,11 @@ | |
| 194 | ssl_global_init(); |
| 195 | |
| 196 | /* Get certificate for current server from global config and |
| 197 | * (if we have it in config) add it to certificate store. |
| 198 | */ |
| 199 | cert = ssl_get_certificate(&trusted); |
| 200 | if ( cert!=NULL ){ |
| 201 | X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert); |
| 202 | X509_free(cert); |
| 203 | hasSavedCertificate = 1; |
| 204 | } |
| @@ -205,11 +205,11 @@ | |
| 205 | |
| 206 | iBio = BIO_new_ssl_connect(sslCtx); |
| 207 | BIO_get_ssl(iBio, &ssl); |
| 208 | |
| 209 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 210 | if( !SSL_set_tlsext_host_name(ssl, g.urlName) ){ |
| 211 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 212 | "continuing without it.\n"); |
| 213 | } |
| 214 | #endif |
| 215 | |
| @@ -218,23 +218,25 @@ | |
| 218 | ssl_set_errmsg("SSL: cannot open SSL (%s)", |
| 219 | ERR_reason_error_string(ERR_get_error())); |
| 220 | return 1; |
| 221 | } |
| 222 | |
| 223 | BIO_set_conn_hostname(iBio, g.urlName); |
| 224 | BIO_set_conn_int_port(iBio, &g.urlPort); |
| 225 | |
| 226 | if( BIO_do_connect(iBio)<=0 ){ |
| 227 | ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", |
| 228 | g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error())); |
| 229 | ssl_close(); |
| 230 | return 1; |
| 231 | } |
| 232 | |
| 233 | if( BIO_do_handshake(iBio)<=0 ) { |
| 234 | ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)", |
| 235 | g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error())); |
| 236 | ssl_close(); |
| 237 | return 1; |
| 238 | } |
| 239 | /* Check if certificate is valid */ |
| 240 | cert = SSL_get_peer_certificate(ssl); |
| @@ -281,11 +283,11 @@ | |
| 281 | " certificates list\n\n" |
| 282 | "If you are not expecting this message, answer no and " |
| 283 | "contact your server\nadministrator.\n\n" |
| 284 | "Accept certificate for host %s (a=always/y/N)? ", |
| 285 | X509_verify_cert_error_string(e), desc, warning, |
| 286 | g.urlName); |
| 287 | BIO_free(mem); |
| 288 | |
| 289 | prompt_user(prompt, &ans); |
| 290 | free(prompt); |
| 291 | cReply = blob_str(&ans)[0]; |
| @@ -302,11 +304,11 @@ | |
| 302 | &ans); |
| 303 | cReply = blob_str(&ans)[0]; |
| 304 | trusted = ( cReply=='a' || cReply=='A' ); |
| 305 | blob_reset(&ans); |
| 306 | } |
| 307 | ssl_save_certificate(cert, trusted); |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | /* Set the Global.zIpAddr variable to the server we are talking to. |
| 312 | ** This is used to populate the ipaddr column of the rcvfrom table, |
| @@ -323,44 +325,44 @@ | |
| 323 | } |
| 324 | |
| 325 | /* |
| 326 | ** Save certificate to global config. |
| 327 | */ |
| 328 | void ssl_save_certificate(X509 *cert, int trusted){ |
| 329 | BIO *mem; |
| 330 | char *zCert, *zHost; |
| 331 | |
| 332 | mem = BIO_new(BIO_s_mem()); |
| 333 | PEM_write_bio_X509(mem, cert); |
| 334 | BIO_write(mem, "", 1); /* nul-terminate mem buffer */ |
| 335 | BIO_get_mem_data(mem, &zCert); |
| 336 | zHost = mprintf("cert:%s", g.urlName); |
| 337 | db_set(zHost, zCert, 1); |
| 338 | free(zHost); |
| 339 | zHost = mprintf("trusted:%s", g.urlName); |
| 340 | db_set_int(zHost, trusted, 1); |
| 341 | free(zHost); |
| 342 | BIO_free(mem); |
| 343 | } |
| 344 | |
| 345 | /* |
| 346 | ** Get certificate for g.urlName from global config. |
| 347 | ** Return NULL if no certificate found. |
| 348 | */ |
| 349 | X509 *ssl_get_certificate(int *pTrusted){ |
| 350 | char *zHost, *zCert; |
| 351 | BIO *mem; |
| 352 | X509 *cert; |
| 353 | |
| 354 | zHost = mprintf("cert:%s", g.urlName); |
| 355 | zCert = db_get(zHost, NULL); |
| 356 | free(zHost); |
| 357 | if ( zCert==NULL ) |
| 358 | return NULL; |
| 359 | |
| 360 | if ( pTrusted!=0 ){ |
| 361 | zHost = mprintf("trusted:%s", g.urlName); |
| 362 | *pTrusted = db_get_int(zHost, 0); |
| 363 | free(zHost); |
| 364 | } |
| 365 | |
| 366 | mem = BIO_new(BIO_s_mem()); |
| 367 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -183,11 +183,11 @@ | |
| 183 | ** g.urlName Name of the server. Ex: www.fossil-scm.org |
| 184 | ** g.urlPort TCP/IP port to use. Ex: 80 |
| 185 | ** |
| 186 | ** Return the number of errors. |
| 187 | */ |
| 188 | int ssl_open(UrlData *pUrlData){ |
| 189 | X509 *cert; |
| 190 | int hasSavedCertificate = 0; |
| 191 | int trusted = 0; |
| 192 | unsigned long e; |
| 193 | |
| @@ -194,11 +194,11 @@ | |
| 194 | ssl_global_init(); |
| 195 | |
| 196 | /* Get certificate for current server from global config and |
| 197 | * (if we have it in config) add it to certificate store. |
| 198 | */ |
| 199 | cert = ssl_get_certificate(pUrlData, &trusted); |
| 200 | if ( cert!=NULL ){ |
| 201 | X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert); |
| 202 | X509_free(cert); |
| 203 | hasSavedCertificate = 1; |
| 204 | } |
| @@ -205,11 +205,11 @@ | |
| 205 | |
| 206 | iBio = BIO_new_ssl_connect(sslCtx); |
| 207 | BIO_get_ssl(iBio, &ssl); |
| 208 | |
| 209 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 210 | if( !SSL_set_tlsext_host_name(ssl, pUrlData->name) ){ |
| 211 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 212 | "continuing without it.\n"); |
| 213 | } |
| 214 | #endif |
| 215 | |
| @@ -218,23 +218,25 @@ | |
| 218 | ssl_set_errmsg("SSL: cannot open SSL (%s)", |
| 219 | ERR_reason_error_string(ERR_get_error())); |
| 220 | return 1; |
| 221 | } |
| 222 | |
| 223 | BIO_set_conn_hostname(iBio, pUrlData->name); |
| 224 | BIO_set_conn_int_port(iBio, &pUrlData->port); |
| 225 | |
| 226 | if( BIO_do_connect(iBio)<=0 ){ |
| 227 | ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", |
| 228 | pUrlData->name, pUrlData->port, |
| 229 | ERR_reason_error_string(ERR_get_error())); |
| 230 | ssl_close(); |
| 231 | return 1; |
| 232 | } |
| 233 | |
| 234 | if( BIO_do_handshake(iBio)<=0 ) { |
| 235 | ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)", |
| 236 | pUrlData->name, pUrlData->port, |
| 237 | ERR_reason_error_string(ERR_get_error())); |
| 238 | ssl_close(); |
| 239 | return 1; |
| 240 | } |
| 241 | /* Check if certificate is valid */ |
| 242 | cert = SSL_get_peer_certificate(ssl); |
| @@ -281,11 +283,11 @@ | |
| 283 | " certificates list\n\n" |
| 284 | "If you are not expecting this message, answer no and " |
| 285 | "contact your server\nadministrator.\n\n" |
| 286 | "Accept certificate for host %s (a=always/y/N)? ", |
| 287 | X509_verify_cert_error_string(e), desc, warning, |
| 288 | pUrlData->name); |
| 289 | BIO_free(mem); |
| 290 | |
| 291 | prompt_user(prompt, &ans); |
| 292 | free(prompt); |
| 293 | cReply = blob_str(&ans)[0]; |
| @@ -302,11 +304,11 @@ | |
| 304 | &ans); |
| 305 | cReply = blob_str(&ans)[0]; |
| 306 | trusted = ( cReply=='a' || cReply=='A' ); |
| 307 | blob_reset(&ans); |
| 308 | } |
| 309 | ssl_save_certificate(pUrlData, cert, trusted); |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | /* Set the Global.zIpAddr variable to the server we are talking to. |
| 314 | ** This is used to populate the ipaddr column of the rcvfrom table, |
| @@ -323,44 +325,44 @@ | |
| 325 | } |
| 326 | |
| 327 | /* |
| 328 | ** Save certificate to global config. |
| 329 | */ |
| 330 | void ssl_save_certificate(UrlData *pUrlData, X509 *cert, int trusted){ |
| 331 | BIO *mem; |
| 332 | char *zCert, *zHost; |
| 333 | |
| 334 | mem = BIO_new(BIO_s_mem()); |
| 335 | PEM_write_bio_X509(mem, cert); |
| 336 | BIO_write(mem, "", 1); /* nul-terminate mem buffer */ |
| 337 | BIO_get_mem_data(mem, &zCert); |
| 338 | zHost = mprintf("cert:%s", pUrlData->name); |
| 339 | db_set(zHost, zCert, 1); |
| 340 | free(zHost); |
| 341 | zHost = mprintf("trusted:%s", pUrlData->name); |
| 342 | db_set_int(zHost, trusted, 1); |
| 343 | free(zHost); |
| 344 | BIO_free(mem); |
| 345 | } |
| 346 | |
| 347 | /* |
| 348 | ** Get certificate for g.urlName from global config. |
| 349 | ** Return NULL if no certificate found. |
| 350 | */ |
| 351 | X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){ |
| 352 | char *zHost, *zCert; |
| 353 | BIO *mem; |
| 354 | X509 *cert; |
| 355 | |
| 356 | zHost = mprintf("cert:%s", pUrlData->name); |
| 357 | zCert = db_get(zHost, NULL); |
| 358 | free(zHost); |
| 359 | if ( zCert==NULL ) |
| 360 | return NULL; |
| 361 | |
| 362 | if ( pTrusted!=0 ){ |
| 363 | zHost = mprintf("trusted:%s", pUrlData->name); |
| 364 | *pTrusted = db_get_int(zHost, 0); |
| 365 | free(zHost); |
| 366 | } |
| 367 | |
| 368 | mem = BIO_new(BIO_s_mem()); |
| 369 |
+46
-46
| --- src/http_transport.c | ||
| +++ src/http_transport.c | ||
| @@ -52,13 +52,13 @@ | ||
| 52 | 52 | |
| 53 | 53 | |
| 54 | 54 | /* |
| 55 | 55 | ** Return the current transport error message. |
| 56 | 56 | */ |
| 57 | -const char *transport_errmsg(void){ | |
| 57 | +const char *transport_errmsg(UrlData *pUrlData){ | |
| 58 | 58 | #ifdef FOSSIL_ENABLE_SSL |
| 59 | - if( g.urlIsHttps ){ | |
| 59 | + if( pUrlData->isHttps ){ | |
| 60 | 60 | return ssl_errmsg(); |
| 61 | 61 | } |
| 62 | 62 | #endif |
| 63 | 63 | return socket_errmsg(); |
| 64 | 64 | } |
| @@ -86,47 +86,47 @@ | ||
| 86 | 86 | #endif |
| 87 | 87 | |
| 88 | 88 | /* |
| 89 | 89 | ** SSH initialization of the transport layer |
| 90 | 90 | */ |
| 91 | -int transport_ssh_open(void){ | |
| 91 | +int transport_ssh_open(UrlData *pUrlData){ | |
| 92 | 92 | /* For SSH we need to create and run SSH fossil http |
| 93 | 93 | ** to talk to the remote machine. |
| 94 | 94 | */ |
| 95 | 95 | const char *zSsh; /* The base SSH command */ |
| 96 | 96 | Blob zCmd; /* The SSH command */ |
| 97 | 97 | char *zHost; /* The host name to contact */ |
| 98 | 98 | int n; /* Size of prefix string */ |
| 99 | 99 | |
| 100 | - socket_ssh_resolve_addr(); | |
| 100 | + socket_ssh_resolve_addr(pUrlData); | |
| 101 | 101 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 102 | 102 | blob_init(&zCmd, zSsh, -1); |
| 103 | - if( g.urlPort!=g.urlDfltPort && g.urlPort ){ | |
| 103 | + if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){ | |
| 104 | 104 | #ifdef __MINGW32__ |
| 105 | - blob_appendf(&zCmd, " -P %d", g.urlPort); | |
| 105 | + blob_appendf(&zCmd, " -P %d", pUrlData->port); | |
| 106 | 106 | #else |
| 107 | - blob_appendf(&zCmd, " -p %d", g.urlPort); | |
| 107 | + blob_appendf(&zCmd, " -p %d", pUrlData->port); | |
| 108 | 108 | #endif |
| 109 | 109 | } |
| 110 | 110 | if( g.fSshTrace ){ |
| 111 | 111 | fossil_force_newline(); |
| 112 | 112 | fossil_print("%s", blob_str(&zCmd)); /* Show the base of the SSH command */ |
| 113 | 113 | } |
| 114 | - if( g.urlUser && g.urlUser[0] ){ | |
| 115 | - zHost = mprintf("%s@%s", g.urlUser, g.urlName); | |
| 114 | + if( pUrlData->user && pUrlData->user[0] ){ | |
| 115 | + zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name); | |
| 116 | 116 | }else{ |
| 117 | - zHost = mprintf("%s", g.urlName); | |
| 117 | + zHost = mprintf("%s", pUrlData->name); | |
| 118 | 118 | } |
| 119 | 119 | n = blob_size(&zCmd); |
| 120 | 120 | blob_append(&zCmd, " ", 1); |
| 121 | 121 | shell_escape(&zCmd, zHost); |
| 122 | 122 | blob_append(&zCmd, " ", 1); |
| 123 | - shell_escape(&zCmd, mprintf("%s", g.urlFossil)); | |
| 123 | + shell_escape(&zCmd, mprintf("%s", pUrlData->fossil)); | |
| 124 | 124 | blob_append(&zCmd, " test-http", 10); |
| 125 | - if( g.urlPath && g.urlPath[0] ){ | |
| 125 | + if( pUrlData->path && pUrlData->path[0] ){ | |
| 126 | 126 | blob_append(&zCmd, " ", 1); |
| 127 | - shell_escape(&zCmd, mprintf("%s", g.urlPath)); | |
| 127 | + shell_escape(&zCmd, mprintf("%s", pUrlData->path)); | |
| 128 | 128 | } |
| 129 | 129 | if( g.fSshTrace ){ |
| 130 | 130 | fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */ |
| 131 | 131 | } |
| 132 | 132 | free(zHost); |
| @@ -146,25 +146,25 @@ | ||
| 146 | 146 | ** g.urlPort TCP/IP port. Ex: 80 |
| 147 | 147 | ** g.urlIsHttps Use TLS for the connection |
| 148 | 148 | ** |
| 149 | 149 | ** Return the number of errors. |
| 150 | 150 | */ |
| 151 | -int transport_open(void){ | |
| 151 | +int transport_open(UrlData *pUrlData){ | |
| 152 | 152 | int rc = 0; |
| 153 | 153 | if( transport.isOpen==0 ){ |
| 154 | - if( g.urlIsSsh ){ | |
| 155 | - rc = transport_ssh_open(); | |
| 154 | + if( pUrlData->isSsh ){ | |
| 155 | + rc = transport_ssh_open(pUrlData); | |
| 156 | 156 | if( rc==0 ) transport.isOpen = 1; |
| 157 | - }else if( g.urlIsHttps ){ | |
| 157 | + }else if( pUrlData->isHttps ){ | |
| 158 | 158 | #ifdef FOSSIL_ENABLE_SSL |
| 159 | - rc = ssl_open(); | |
| 159 | + rc = ssl_open(pUrlData); | |
| 160 | 160 | if( rc==0 ) transport.isOpen = 1; |
| 161 | 161 | #else |
| 162 | 162 | socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support"); |
| 163 | 163 | rc = 1; |
| 164 | 164 | #endif |
| 165 | - }else if( g.urlIsFile ){ | |
| 165 | + }else if( pUrlData->isFile ){ | |
| 166 | 166 | sqlite3_uint64 iRandId; |
| 167 | 167 | sqlite3_randomness(sizeof(iRandId), &iRandId); |
| 168 | 168 | transport.zOutFile = mprintf("%s-%llu-out.http", |
| 169 | 169 | g.zRepositoryName, iRandId); |
| 170 | 170 | transport.zInFile = mprintf("%s-%llu-in.http", |
| @@ -173,21 +173,21 @@ | ||
| 173 | 173 | if( transport.pFile==0 ){ |
| 174 | 174 | fossil_fatal("cannot output temporary file: %s", transport.zOutFile); |
| 175 | 175 | } |
| 176 | 176 | transport.isOpen = 1; |
| 177 | 177 | }else{ |
| 178 | - rc = socket_open(); | |
| 178 | + rc = socket_open(pUrlData); | |
| 179 | 179 | if( rc==0 ) transport.isOpen = 1; |
| 180 | 180 | } |
| 181 | 181 | } |
| 182 | 182 | return rc; |
| 183 | 183 | } |
| 184 | 184 | |
| 185 | 185 | /* |
| 186 | 186 | ** Close the current connection |
| 187 | 187 | */ |
| 188 | -void transport_close(void){ | |
| 188 | +void transport_close(UrlData *pUrlData){ | |
| 189 | 189 | if( transport.isOpen ){ |
| 190 | 190 | free(transport.pBuf); |
| 191 | 191 | transport.pBuf = 0; |
| 192 | 192 | transport.nAlloc = 0; |
| 193 | 193 | transport.nUsed = 0; |
| @@ -194,17 +194,17 @@ | ||
| 194 | 194 | transport.iCursor = 0; |
| 195 | 195 | if( transport.pLog ){ |
| 196 | 196 | fclose(transport.pLog); |
| 197 | 197 | transport.pLog = 0; |
| 198 | 198 | } |
| 199 | - if( g.urlIsSsh ){ | |
| 199 | + if( pUrlData->isSsh ){ | |
| 200 | 200 | transport_ssh_close(); |
| 201 | - }else if( g.urlIsHttps ){ | |
| 201 | + }else if( pUrlData->isHttps ){ | |
| 202 | 202 | #ifdef FOSSIL_ENABLE_SSL |
| 203 | 203 | ssl_close(); |
| 204 | 204 | #endif |
| 205 | - }else if( g.urlIsFile ){ | |
| 205 | + }else if( pUrlData->isFile ){ | |
| 206 | 206 | if( transport.pFile ){ |
| 207 | 207 | fclose(transport.pFile); |
| 208 | 208 | transport.pFile = 0; |
| 209 | 209 | } |
| 210 | 210 | file_delete(transport.zInFile); |
| @@ -219,28 +219,28 @@ | ||
| 219 | 219 | } |
| 220 | 220 | |
| 221 | 221 | /* |
| 222 | 222 | ** Send content over the wire. |
| 223 | 223 | */ |
| 224 | -void transport_send(Blob *toSend){ | |
| 224 | +void transport_send(UrlData *pUrlData, Blob *toSend){ | |
| 225 | 225 | char *z = blob_buffer(toSend); |
| 226 | 226 | int n = blob_size(toSend); |
| 227 | 227 | transport.nSent += n; |
| 228 | - if( g.urlIsSsh ){ | |
| 228 | + if( pUrlData->isSsh ){ | |
| 229 | 229 | fwrite(z, 1, n, sshOut); |
| 230 | 230 | fflush(sshOut); |
| 231 | - }else if( g.urlIsHttps ){ | |
| 231 | + }else if( pUrlData->isHttps ){ | |
| 232 | 232 | #ifdef FOSSIL_ENABLE_SSL |
| 233 | 233 | int sent; |
| 234 | 234 | while( n>0 ){ |
| 235 | 235 | sent = ssl_send(0, z, n); |
| 236 | 236 | /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */ |
| 237 | 237 | if( sent<=0 ) break; |
| 238 | 238 | n -= sent; |
| 239 | 239 | } |
| 240 | 240 | #endif |
| 241 | - }else if( g.urlIsFile ){ | |
| 241 | + }else if( pUrlData->isFile ){ | |
| 242 | 242 | fwrite(z, 1, n, transport.pFile); |
| 243 | 243 | }else{ |
| 244 | 244 | int sent; |
| 245 | 245 | while( n>0 ){ |
| 246 | 246 | sent = socket_send(0, z, n); |
| @@ -253,16 +253,16 @@ | ||
| 253 | 253 | |
| 254 | 254 | /* |
| 255 | 255 | ** This routine is called when the outbound message is complete and |
| 256 | 256 | ** it is time to being receiving a reply. |
| 257 | 257 | */ |
| 258 | -void transport_flip(void){ | |
| 259 | - if( g.urlIsFile ){ | |
| 258 | +void transport_flip(UrlData *pUrlData){ | |
| 259 | + if( pUrlData->isFile ){ | |
| 260 | 260 | char *zCmd; |
| 261 | 261 | fclose(transport.pFile); |
| 262 | 262 | zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth", |
| 263 | - g.nameOfExe, g.urlName, transport.zOutFile, transport.zInFile | |
| 263 | + g.nameOfExe, pUrlData->name, transport.zOutFile, transport.zInFile | |
| 264 | 264 | ); |
| 265 | 265 | fossil_system(zCmd); |
| 266 | 266 | free(zCmd); |
| 267 | 267 | transport.pFile = fossil_fopen(transport.zInFile, "rb"); |
| 268 | 268 | } |
| @@ -282,21 +282,21 @@ | ||
| 282 | 282 | |
| 283 | 283 | /* |
| 284 | 284 | ** This routine is called when the inbound message has been received |
| 285 | 285 | ** and it is time to start sending again. |
| 286 | 286 | */ |
| 287 | -void transport_rewind(void){ | |
| 288 | - if( g.urlIsFile ){ | |
| 289 | - transport_close(); | |
| 287 | +void transport_rewind(UrlData *pUrlData){ | |
| 288 | + if( pUrlData->isFile ){ | |
| 289 | + transport_close(pUrlData); | |
| 290 | 290 | } |
| 291 | 291 | } |
| 292 | 292 | |
| 293 | 293 | /* |
| 294 | 294 | ** Read N bytes of content directly from the wire and write into |
| 295 | 295 | ** the buffer. |
| 296 | 296 | */ |
| 297 | -static int transport_fetch(char *zBuf, int N){ | |
| 297 | +static int transport_fetch(UrlData *pUrlData, char *zBuf, int N){ | |
| 298 | 298 | int got; |
| 299 | 299 | if( sshIn ){ |
| 300 | 300 | int x; |
| 301 | 301 | int wanted = N; |
| 302 | 302 | got = 0; |
| @@ -304,17 +304,17 @@ | ||
| 304 | 304 | x = read(sshIn, &zBuf[got], wanted); |
| 305 | 305 | if( x<=0 ) break; |
| 306 | 306 | got += x; |
| 307 | 307 | wanted -= x; |
| 308 | 308 | } |
| 309 | - }else if( g.urlIsHttps ){ | |
| 309 | + }else if( pUrlData->isHttps ){ | |
| 310 | 310 | #ifdef FOSSIL_ENABLE_SSL |
| 311 | 311 | got = ssl_receive(0, zBuf, N); |
| 312 | 312 | #else |
| 313 | 313 | got = 0; |
| 314 | 314 | #endif |
| 315 | - }else if( g.urlIsFile ){ | |
| 315 | + }else if( pUrlData->isFile ){ | |
| 316 | 316 | got = fread(zBuf, 1, N, transport.pFile); |
| 317 | 317 | }else{ |
| 318 | 318 | got = socket_receive(0, zBuf, N); |
| 319 | 319 | } |
| 320 | 320 | /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ |
| @@ -327,11 +327,11 @@ | ||
| 327 | 327 | |
| 328 | 328 | /* |
| 329 | 329 | ** Read N bytes of content from the wire and store in the supplied buffer. |
| 330 | 330 | ** Return the number of bytes actually received. |
| 331 | 331 | */ |
| 332 | -int transport_receive(char *zBuf, int N){ | |
| 332 | +int transport_receive(UrlData *pUrlData, char *zBuf, int N){ | |
| 333 | 333 | int onHand; /* Bytes current held in the transport buffer */ |
| 334 | 334 | int nByte = 0; /* Bytes of content received */ |
| 335 | 335 | |
| 336 | 336 | onHand = transport.nUsed - transport.iCursor; |
| 337 | 337 | if( g.fSshTrace){ |
| @@ -351,11 +351,11 @@ | ||
| 351 | 351 | N -= toMove; |
| 352 | 352 | zBuf += toMove; |
| 353 | 353 | nByte += toMove; |
| 354 | 354 | } |
| 355 | 355 | if( N>0 ){ |
| 356 | - int got = transport_fetch(zBuf, N); | |
| 356 | + int got = transport_fetch(pUrlData, zBuf, N); | |
| 357 | 357 | if( got>0 ){ |
| 358 | 358 | nByte += got; |
| 359 | 359 | transport.nRcvd += got; |
| 360 | 360 | } |
| 361 | 361 | } |
| @@ -366,11 +366,11 @@ | ||
| 366 | 366 | /* |
| 367 | 367 | ** Load up to N new bytes of content into the transport.pBuf buffer. |
| 368 | 368 | ** The buffer itself might be moved. And the transport.iCursor value |
| 369 | 369 | ** might be reset to 0. |
| 370 | 370 | */ |
| 371 | -static void transport_load_buffer(int N){ | |
| 371 | +static void transport_load_buffer(UrlData *pUrlData, int N){ | |
| 372 | 372 | int i, j; |
| 373 | 373 | if( transport.nAlloc==0 ){ |
| 374 | 374 | transport.nAlloc = N; |
| 375 | 375 | transport.pBuf = fossil_malloc( N ); |
| 376 | 376 | transport.iCursor = 0; |
| @@ -388,11 +388,11 @@ | ||
| 388 | 388 | transport.nAlloc = transport.nUsed + N; |
| 389 | 389 | pNew = fossil_realloc(transport.pBuf, transport.nAlloc); |
| 390 | 390 | transport.pBuf = pNew; |
| 391 | 391 | } |
| 392 | 392 | if( N>0 ){ |
| 393 | - i = transport_fetch(&transport.pBuf[transport.nUsed], N); | |
| 393 | + i = transport_fetch(pUrlData, &transport.pBuf[transport.nUsed], N); | |
| 394 | 394 | if( i>0 ){ |
| 395 | 395 | transport.nRcvd += i; |
| 396 | 396 | transport.nUsed += i; |
| 397 | 397 | } |
| 398 | 398 | } |
| @@ -404,18 +404,18 @@ | ||
| 404 | 404 | ** from the received line and zero-terminate the result. Return a pointer |
| 405 | 405 | ** to the line. |
| 406 | 406 | ** |
| 407 | 407 | ** Each call to this routine potentially overwrites the returned buffer. |
| 408 | 408 | */ |
| 409 | -char *transport_receive_line(void){ | |
| 409 | +char *transport_receive_line(UrlData *pUrlData){ | |
| 410 | 410 | int i; |
| 411 | 411 | int iStart; |
| 412 | 412 | |
| 413 | 413 | i = iStart = transport.iCursor; |
| 414 | 414 | while(1){ |
| 415 | 415 | if( i >= transport.nUsed ){ |
| 416 | - transport_load_buffer(g.urlIsSsh ? 2 : 1000); | |
| 416 | + transport_load_buffer(pUrlData, pUrlData->isSsh ? 2 : 1000); | |
| 417 | 417 | i -= iStart; |
| 418 | 418 | iStart = 0; |
| 419 | 419 | if( i >= transport.nUsed ){ |
| 420 | 420 | transport.pBuf[i] = 0; |
| 421 | 421 | transport.iCursor = i; |
| @@ -437,15 +437,15 @@ | ||
| 437 | 437 | } |
| 438 | 438 | |
| 439 | 439 | /* |
| 440 | 440 | ** Global transport shutdown |
| 441 | 441 | */ |
| 442 | -void transport_global_shutdown(void){ | |
| 443 | - if( g.urlIsSsh ){ | |
| 442 | +void transport_global_shutdown(UrlData *pUrlData){ | |
| 443 | + if( pUrlData->isSsh ){ | |
| 444 | 444 | transport_ssh_close(); |
| 445 | 445 | } |
| 446 | - if( g.urlIsHttps ){ | |
| 446 | + if( pUrlData->isHttps ){ | |
| 447 | 447 | #ifdef FOSSIL_ENABLE_SSL |
| 448 | 448 | ssl_global_shutdown(); |
| 449 | 449 | #endif |
| 450 | 450 | }else{ |
| 451 | 451 | socket_global_shutdown(); |
| 452 | 452 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -52,13 +52,13 @@ | |
| 52 | |
| 53 | |
| 54 | /* |
| 55 | ** Return the current transport error message. |
| 56 | */ |
| 57 | const char *transport_errmsg(void){ |
| 58 | #ifdef FOSSIL_ENABLE_SSL |
| 59 | if( g.urlIsHttps ){ |
| 60 | return ssl_errmsg(); |
| 61 | } |
| 62 | #endif |
| 63 | return socket_errmsg(); |
| 64 | } |
| @@ -86,47 +86,47 @@ | |
| 86 | #endif |
| 87 | |
| 88 | /* |
| 89 | ** SSH initialization of the transport layer |
| 90 | */ |
| 91 | int transport_ssh_open(void){ |
| 92 | /* For SSH we need to create and run SSH fossil http |
| 93 | ** to talk to the remote machine. |
| 94 | */ |
| 95 | const char *zSsh; /* The base SSH command */ |
| 96 | Blob zCmd; /* The SSH command */ |
| 97 | char *zHost; /* The host name to contact */ |
| 98 | int n; /* Size of prefix string */ |
| 99 | |
| 100 | socket_ssh_resolve_addr(); |
| 101 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 102 | blob_init(&zCmd, zSsh, -1); |
| 103 | if( g.urlPort!=g.urlDfltPort && g.urlPort ){ |
| 104 | #ifdef __MINGW32__ |
| 105 | blob_appendf(&zCmd, " -P %d", g.urlPort); |
| 106 | #else |
| 107 | blob_appendf(&zCmd, " -p %d", g.urlPort); |
| 108 | #endif |
| 109 | } |
| 110 | if( g.fSshTrace ){ |
| 111 | fossil_force_newline(); |
| 112 | fossil_print("%s", blob_str(&zCmd)); /* Show the base of the SSH command */ |
| 113 | } |
| 114 | if( g.urlUser && g.urlUser[0] ){ |
| 115 | zHost = mprintf("%s@%s", g.urlUser, g.urlName); |
| 116 | }else{ |
| 117 | zHost = mprintf("%s", g.urlName); |
| 118 | } |
| 119 | n = blob_size(&zCmd); |
| 120 | blob_append(&zCmd, " ", 1); |
| 121 | shell_escape(&zCmd, zHost); |
| 122 | blob_append(&zCmd, " ", 1); |
| 123 | shell_escape(&zCmd, mprintf("%s", g.urlFossil)); |
| 124 | blob_append(&zCmd, " test-http", 10); |
| 125 | if( g.urlPath && g.urlPath[0] ){ |
| 126 | blob_append(&zCmd, " ", 1); |
| 127 | shell_escape(&zCmd, mprintf("%s", g.urlPath)); |
| 128 | } |
| 129 | if( g.fSshTrace ){ |
| 130 | fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */ |
| 131 | } |
| 132 | free(zHost); |
| @@ -146,25 +146,25 @@ | |
| 146 | ** g.urlPort TCP/IP port. Ex: 80 |
| 147 | ** g.urlIsHttps Use TLS for the connection |
| 148 | ** |
| 149 | ** Return the number of errors. |
| 150 | */ |
| 151 | int transport_open(void){ |
| 152 | int rc = 0; |
| 153 | if( transport.isOpen==0 ){ |
| 154 | if( g.urlIsSsh ){ |
| 155 | rc = transport_ssh_open(); |
| 156 | if( rc==0 ) transport.isOpen = 1; |
| 157 | }else if( g.urlIsHttps ){ |
| 158 | #ifdef FOSSIL_ENABLE_SSL |
| 159 | rc = ssl_open(); |
| 160 | if( rc==0 ) transport.isOpen = 1; |
| 161 | #else |
| 162 | socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support"); |
| 163 | rc = 1; |
| 164 | #endif |
| 165 | }else if( g.urlIsFile ){ |
| 166 | sqlite3_uint64 iRandId; |
| 167 | sqlite3_randomness(sizeof(iRandId), &iRandId); |
| 168 | transport.zOutFile = mprintf("%s-%llu-out.http", |
| 169 | g.zRepositoryName, iRandId); |
| 170 | transport.zInFile = mprintf("%s-%llu-in.http", |
| @@ -173,21 +173,21 @@ | |
| 173 | if( transport.pFile==0 ){ |
| 174 | fossil_fatal("cannot output temporary file: %s", transport.zOutFile); |
| 175 | } |
| 176 | transport.isOpen = 1; |
| 177 | }else{ |
| 178 | rc = socket_open(); |
| 179 | if( rc==0 ) transport.isOpen = 1; |
| 180 | } |
| 181 | } |
| 182 | return rc; |
| 183 | } |
| 184 | |
| 185 | /* |
| 186 | ** Close the current connection |
| 187 | */ |
| 188 | void transport_close(void){ |
| 189 | if( transport.isOpen ){ |
| 190 | free(transport.pBuf); |
| 191 | transport.pBuf = 0; |
| 192 | transport.nAlloc = 0; |
| 193 | transport.nUsed = 0; |
| @@ -194,17 +194,17 @@ | |
| 194 | transport.iCursor = 0; |
| 195 | if( transport.pLog ){ |
| 196 | fclose(transport.pLog); |
| 197 | transport.pLog = 0; |
| 198 | } |
| 199 | if( g.urlIsSsh ){ |
| 200 | transport_ssh_close(); |
| 201 | }else if( g.urlIsHttps ){ |
| 202 | #ifdef FOSSIL_ENABLE_SSL |
| 203 | ssl_close(); |
| 204 | #endif |
| 205 | }else if( g.urlIsFile ){ |
| 206 | if( transport.pFile ){ |
| 207 | fclose(transport.pFile); |
| 208 | transport.pFile = 0; |
| 209 | } |
| 210 | file_delete(transport.zInFile); |
| @@ -219,28 +219,28 @@ | |
| 219 | } |
| 220 | |
| 221 | /* |
| 222 | ** Send content over the wire. |
| 223 | */ |
| 224 | void transport_send(Blob *toSend){ |
| 225 | char *z = blob_buffer(toSend); |
| 226 | int n = blob_size(toSend); |
| 227 | transport.nSent += n; |
| 228 | if( g.urlIsSsh ){ |
| 229 | fwrite(z, 1, n, sshOut); |
| 230 | fflush(sshOut); |
| 231 | }else if( g.urlIsHttps ){ |
| 232 | #ifdef FOSSIL_ENABLE_SSL |
| 233 | int sent; |
| 234 | while( n>0 ){ |
| 235 | sent = ssl_send(0, z, n); |
| 236 | /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */ |
| 237 | if( sent<=0 ) break; |
| 238 | n -= sent; |
| 239 | } |
| 240 | #endif |
| 241 | }else if( g.urlIsFile ){ |
| 242 | fwrite(z, 1, n, transport.pFile); |
| 243 | }else{ |
| 244 | int sent; |
| 245 | while( n>0 ){ |
| 246 | sent = socket_send(0, z, n); |
| @@ -253,16 +253,16 @@ | |
| 253 | |
| 254 | /* |
| 255 | ** This routine is called when the outbound message is complete and |
| 256 | ** it is time to being receiving a reply. |
| 257 | */ |
| 258 | void transport_flip(void){ |
| 259 | if( g.urlIsFile ){ |
| 260 | char *zCmd; |
| 261 | fclose(transport.pFile); |
| 262 | zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth", |
| 263 | g.nameOfExe, g.urlName, transport.zOutFile, transport.zInFile |
| 264 | ); |
| 265 | fossil_system(zCmd); |
| 266 | free(zCmd); |
| 267 | transport.pFile = fossil_fopen(transport.zInFile, "rb"); |
| 268 | } |
| @@ -282,21 +282,21 @@ | |
| 282 | |
| 283 | /* |
| 284 | ** This routine is called when the inbound message has been received |
| 285 | ** and it is time to start sending again. |
| 286 | */ |
| 287 | void transport_rewind(void){ |
| 288 | if( g.urlIsFile ){ |
| 289 | transport_close(); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | /* |
| 294 | ** Read N bytes of content directly from the wire and write into |
| 295 | ** the buffer. |
| 296 | */ |
| 297 | static int transport_fetch(char *zBuf, int N){ |
| 298 | int got; |
| 299 | if( sshIn ){ |
| 300 | int x; |
| 301 | int wanted = N; |
| 302 | got = 0; |
| @@ -304,17 +304,17 @@ | |
| 304 | x = read(sshIn, &zBuf[got], wanted); |
| 305 | if( x<=0 ) break; |
| 306 | got += x; |
| 307 | wanted -= x; |
| 308 | } |
| 309 | }else if( g.urlIsHttps ){ |
| 310 | #ifdef FOSSIL_ENABLE_SSL |
| 311 | got = ssl_receive(0, zBuf, N); |
| 312 | #else |
| 313 | got = 0; |
| 314 | #endif |
| 315 | }else if( g.urlIsFile ){ |
| 316 | got = fread(zBuf, 1, N, transport.pFile); |
| 317 | }else{ |
| 318 | got = socket_receive(0, zBuf, N); |
| 319 | } |
| 320 | /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ |
| @@ -327,11 +327,11 @@ | |
| 327 | |
| 328 | /* |
| 329 | ** Read N bytes of content from the wire and store in the supplied buffer. |
| 330 | ** Return the number of bytes actually received. |
| 331 | */ |
| 332 | int transport_receive(char *zBuf, int N){ |
| 333 | int onHand; /* Bytes current held in the transport buffer */ |
| 334 | int nByte = 0; /* Bytes of content received */ |
| 335 | |
| 336 | onHand = transport.nUsed - transport.iCursor; |
| 337 | if( g.fSshTrace){ |
| @@ -351,11 +351,11 @@ | |
| 351 | N -= toMove; |
| 352 | zBuf += toMove; |
| 353 | nByte += toMove; |
| 354 | } |
| 355 | if( N>0 ){ |
| 356 | int got = transport_fetch(zBuf, N); |
| 357 | if( got>0 ){ |
| 358 | nByte += got; |
| 359 | transport.nRcvd += got; |
| 360 | } |
| 361 | } |
| @@ -366,11 +366,11 @@ | |
| 366 | /* |
| 367 | ** Load up to N new bytes of content into the transport.pBuf buffer. |
| 368 | ** The buffer itself might be moved. And the transport.iCursor value |
| 369 | ** might be reset to 0. |
| 370 | */ |
| 371 | static void transport_load_buffer(int N){ |
| 372 | int i, j; |
| 373 | if( transport.nAlloc==0 ){ |
| 374 | transport.nAlloc = N; |
| 375 | transport.pBuf = fossil_malloc( N ); |
| 376 | transport.iCursor = 0; |
| @@ -388,11 +388,11 @@ | |
| 388 | transport.nAlloc = transport.nUsed + N; |
| 389 | pNew = fossil_realloc(transport.pBuf, transport.nAlloc); |
| 390 | transport.pBuf = pNew; |
| 391 | } |
| 392 | if( N>0 ){ |
| 393 | i = transport_fetch(&transport.pBuf[transport.nUsed], N); |
| 394 | if( i>0 ){ |
| 395 | transport.nRcvd += i; |
| 396 | transport.nUsed += i; |
| 397 | } |
| 398 | } |
| @@ -404,18 +404,18 @@ | |
| 404 | ** from the received line and zero-terminate the result. Return a pointer |
| 405 | ** to the line. |
| 406 | ** |
| 407 | ** Each call to this routine potentially overwrites the returned buffer. |
| 408 | */ |
| 409 | char *transport_receive_line(void){ |
| 410 | int i; |
| 411 | int iStart; |
| 412 | |
| 413 | i = iStart = transport.iCursor; |
| 414 | while(1){ |
| 415 | if( i >= transport.nUsed ){ |
| 416 | transport_load_buffer(g.urlIsSsh ? 2 : 1000); |
| 417 | i -= iStart; |
| 418 | iStart = 0; |
| 419 | if( i >= transport.nUsed ){ |
| 420 | transport.pBuf[i] = 0; |
| 421 | transport.iCursor = i; |
| @@ -437,15 +437,15 @@ | |
| 437 | } |
| 438 | |
| 439 | /* |
| 440 | ** Global transport shutdown |
| 441 | */ |
| 442 | void transport_global_shutdown(void){ |
| 443 | if( g.urlIsSsh ){ |
| 444 | transport_ssh_close(); |
| 445 | } |
| 446 | if( g.urlIsHttps ){ |
| 447 | #ifdef FOSSIL_ENABLE_SSL |
| 448 | ssl_global_shutdown(); |
| 449 | #endif |
| 450 | }else{ |
| 451 | socket_global_shutdown(); |
| 452 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -52,13 +52,13 @@ | |
| 52 | |
| 53 | |
| 54 | /* |
| 55 | ** Return the current transport error message. |
| 56 | */ |
| 57 | const char *transport_errmsg(UrlData *pUrlData){ |
| 58 | #ifdef FOSSIL_ENABLE_SSL |
| 59 | if( pUrlData->isHttps ){ |
| 60 | return ssl_errmsg(); |
| 61 | } |
| 62 | #endif |
| 63 | return socket_errmsg(); |
| 64 | } |
| @@ -86,47 +86,47 @@ | |
| 86 | #endif |
| 87 | |
| 88 | /* |
| 89 | ** SSH initialization of the transport layer |
| 90 | */ |
| 91 | int transport_ssh_open(UrlData *pUrlData){ |
| 92 | /* For SSH we need to create and run SSH fossil http |
| 93 | ** to talk to the remote machine. |
| 94 | */ |
| 95 | const char *zSsh; /* The base SSH command */ |
| 96 | Blob zCmd; /* The SSH command */ |
| 97 | char *zHost; /* The host name to contact */ |
| 98 | int n; /* Size of prefix string */ |
| 99 | |
| 100 | socket_ssh_resolve_addr(pUrlData); |
| 101 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 102 | blob_init(&zCmd, zSsh, -1); |
| 103 | if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){ |
| 104 | #ifdef __MINGW32__ |
| 105 | blob_appendf(&zCmd, " -P %d", pUrlData->port); |
| 106 | #else |
| 107 | blob_appendf(&zCmd, " -p %d", pUrlData->port); |
| 108 | #endif |
| 109 | } |
| 110 | if( g.fSshTrace ){ |
| 111 | fossil_force_newline(); |
| 112 | fossil_print("%s", blob_str(&zCmd)); /* Show the base of the SSH command */ |
| 113 | } |
| 114 | if( pUrlData->user && pUrlData->user[0] ){ |
| 115 | zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name); |
| 116 | }else{ |
| 117 | zHost = mprintf("%s", pUrlData->name); |
| 118 | } |
| 119 | n = blob_size(&zCmd); |
| 120 | blob_append(&zCmd, " ", 1); |
| 121 | shell_escape(&zCmd, zHost); |
| 122 | blob_append(&zCmd, " ", 1); |
| 123 | shell_escape(&zCmd, mprintf("%s", pUrlData->fossil)); |
| 124 | blob_append(&zCmd, " test-http", 10); |
| 125 | if( pUrlData->path && pUrlData->path[0] ){ |
| 126 | blob_append(&zCmd, " ", 1); |
| 127 | shell_escape(&zCmd, mprintf("%s", pUrlData->path)); |
| 128 | } |
| 129 | if( g.fSshTrace ){ |
| 130 | fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */ |
| 131 | } |
| 132 | free(zHost); |
| @@ -146,25 +146,25 @@ | |
| 146 | ** g.urlPort TCP/IP port. Ex: 80 |
| 147 | ** g.urlIsHttps Use TLS for the connection |
| 148 | ** |
| 149 | ** Return the number of errors. |
| 150 | */ |
| 151 | int transport_open(UrlData *pUrlData){ |
| 152 | int rc = 0; |
| 153 | if( transport.isOpen==0 ){ |
| 154 | if( pUrlData->isSsh ){ |
| 155 | rc = transport_ssh_open(pUrlData); |
| 156 | if( rc==0 ) transport.isOpen = 1; |
| 157 | }else if( pUrlData->isHttps ){ |
| 158 | #ifdef FOSSIL_ENABLE_SSL |
| 159 | rc = ssl_open(pUrlData); |
| 160 | if( rc==0 ) transport.isOpen = 1; |
| 161 | #else |
| 162 | socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support"); |
| 163 | rc = 1; |
| 164 | #endif |
| 165 | }else if( pUrlData->isFile ){ |
| 166 | sqlite3_uint64 iRandId; |
| 167 | sqlite3_randomness(sizeof(iRandId), &iRandId); |
| 168 | transport.zOutFile = mprintf("%s-%llu-out.http", |
| 169 | g.zRepositoryName, iRandId); |
| 170 | transport.zInFile = mprintf("%s-%llu-in.http", |
| @@ -173,21 +173,21 @@ | |
| 173 | if( transport.pFile==0 ){ |
| 174 | fossil_fatal("cannot output temporary file: %s", transport.zOutFile); |
| 175 | } |
| 176 | transport.isOpen = 1; |
| 177 | }else{ |
| 178 | rc = socket_open(pUrlData); |
| 179 | if( rc==0 ) transport.isOpen = 1; |
| 180 | } |
| 181 | } |
| 182 | return rc; |
| 183 | } |
| 184 | |
| 185 | /* |
| 186 | ** Close the current connection |
| 187 | */ |
| 188 | void transport_close(UrlData *pUrlData){ |
| 189 | if( transport.isOpen ){ |
| 190 | free(transport.pBuf); |
| 191 | transport.pBuf = 0; |
| 192 | transport.nAlloc = 0; |
| 193 | transport.nUsed = 0; |
| @@ -194,17 +194,17 @@ | |
| 194 | transport.iCursor = 0; |
| 195 | if( transport.pLog ){ |
| 196 | fclose(transport.pLog); |
| 197 | transport.pLog = 0; |
| 198 | } |
| 199 | if( pUrlData->isSsh ){ |
| 200 | transport_ssh_close(); |
| 201 | }else if( pUrlData->isHttps ){ |
| 202 | #ifdef FOSSIL_ENABLE_SSL |
| 203 | ssl_close(); |
| 204 | #endif |
| 205 | }else if( pUrlData->isFile ){ |
| 206 | if( transport.pFile ){ |
| 207 | fclose(transport.pFile); |
| 208 | transport.pFile = 0; |
| 209 | } |
| 210 | file_delete(transport.zInFile); |
| @@ -219,28 +219,28 @@ | |
| 219 | } |
| 220 | |
| 221 | /* |
| 222 | ** Send content over the wire. |
| 223 | */ |
| 224 | void transport_send(UrlData *pUrlData, Blob *toSend){ |
| 225 | char *z = blob_buffer(toSend); |
| 226 | int n = blob_size(toSend); |
| 227 | transport.nSent += n; |
| 228 | if( pUrlData->isSsh ){ |
| 229 | fwrite(z, 1, n, sshOut); |
| 230 | fflush(sshOut); |
| 231 | }else if( pUrlData->isHttps ){ |
| 232 | #ifdef FOSSIL_ENABLE_SSL |
| 233 | int sent; |
| 234 | while( n>0 ){ |
| 235 | sent = ssl_send(0, z, n); |
| 236 | /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */ |
| 237 | if( sent<=0 ) break; |
| 238 | n -= sent; |
| 239 | } |
| 240 | #endif |
| 241 | }else if( pUrlData->isFile ){ |
| 242 | fwrite(z, 1, n, transport.pFile); |
| 243 | }else{ |
| 244 | int sent; |
| 245 | while( n>0 ){ |
| 246 | sent = socket_send(0, z, n); |
| @@ -253,16 +253,16 @@ | |
| 253 | |
| 254 | /* |
| 255 | ** This routine is called when the outbound message is complete and |
| 256 | ** it is time to being receiving a reply. |
| 257 | */ |
| 258 | void transport_flip(UrlData *pUrlData){ |
| 259 | if( pUrlData->isFile ){ |
| 260 | char *zCmd; |
| 261 | fclose(transport.pFile); |
| 262 | zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth", |
| 263 | g.nameOfExe, pUrlData->name, transport.zOutFile, transport.zInFile |
| 264 | ); |
| 265 | fossil_system(zCmd); |
| 266 | free(zCmd); |
| 267 | transport.pFile = fossil_fopen(transport.zInFile, "rb"); |
| 268 | } |
| @@ -282,21 +282,21 @@ | |
| 282 | |
| 283 | /* |
| 284 | ** This routine is called when the inbound message has been received |
| 285 | ** and it is time to start sending again. |
| 286 | */ |
| 287 | void transport_rewind(UrlData *pUrlData){ |
| 288 | if( pUrlData->isFile ){ |
| 289 | transport_close(pUrlData); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | /* |
| 294 | ** Read N bytes of content directly from the wire and write into |
| 295 | ** the buffer. |
| 296 | */ |
| 297 | static int transport_fetch(UrlData *pUrlData, char *zBuf, int N){ |
| 298 | int got; |
| 299 | if( sshIn ){ |
| 300 | int x; |
| 301 | int wanted = N; |
| 302 | got = 0; |
| @@ -304,17 +304,17 @@ | |
| 304 | x = read(sshIn, &zBuf[got], wanted); |
| 305 | if( x<=0 ) break; |
| 306 | got += x; |
| 307 | wanted -= x; |
| 308 | } |
| 309 | }else if( pUrlData->isHttps ){ |
| 310 | #ifdef FOSSIL_ENABLE_SSL |
| 311 | got = ssl_receive(0, zBuf, N); |
| 312 | #else |
| 313 | got = 0; |
| 314 | #endif |
| 315 | }else if( pUrlData->isFile ){ |
| 316 | got = fread(zBuf, 1, N, transport.pFile); |
| 317 | }else{ |
| 318 | got = socket_receive(0, zBuf, N); |
| 319 | } |
| 320 | /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ |
| @@ -327,11 +327,11 @@ | |
| 327 | |
| 328 | /* |
| 329 | ** Read N bytes of content from the wire and store in the supplied buffer. |
| 330 | ** Return the number of bytes actually received. |
| 331 | */ |
| 332 | int transport_receive(UrlData *pUrlData, char *zBuf, int N){ |
| 333 | int onHand; /* Bytes current held in the transport buffer */ |
| 334 | int nByte = 0; /* Bytes of content received */ |
| 335 | |
| 336 | onHand = transport.nUsed - transport.iCursor; |
| 337 | if( g.fSshTrace){ |
| @@ -351,11 +351,11 @@ | |
| 351 | N -= toMove; |
| 352 | zBuf += toMove; |
| 353 | nByte += toMove; |
| 354 | } |
| 355 | if( N>0 ){ |
| 356 | int got = transport_fetch(pUrlData, zBuf, N); |
| 357 | if( got>0 ){ |
| 358 | nByte += got; |
| 359 | transport.nRcvd += got; |
| 360 | } |
| 361 | } |
| @@ -366,11 +366,11 @@ | |
| 366 | /* |
| 367 | ** Load up to N new bytes of content into the transport.pBuf buffer. |
| 368 | ** The buffer itself might be moved. And the transport.iCursor value |
| 369 | ** might be reset to 0. |
| 370 | */ |
| 371 | static void transport_load_buffer(UrlData *pUrlData, int N){ |
| 372 | int i, j; |
| 373 | if( transport.nAlloc==0 ){ |
| 374 | transport.nAlloc = N; |
| 375 | transport.pBuf = fossil_malloc( N ); |
| 376 | transport.iCursor = 0; |
| @@ -388,11 +388,11 @@ | |
| 388 | transport.nAlloc = transport.nUsed + N; |
| 389 | pNew = fossil_realloc(transport.pBuf, transport.nAlloc); |
| 390 | transport.pBuf = pNew; |
| 391 | } |
| 392 | if( N>0 ){ |
| 393 | i = transport_fetch(pUrlData, &transport.pBuf[transport.nUsed], N); |
| 394 | if( i>0 ){ |
| 395 | transport.nRcvd += i; |
| 396 | transport.nUsed += i; |
| 397 | } |
| 398 | } |
| @@ -404,18 +404,18 @@ | |
| 404 | ** from the received line and zero-terminate the result. Return a pointer |
| 405 | ** to the line. |
| 406 | ** |
| 407 | ** Each call to this routine potentially overwrites the returned buffer. |
| 408 | */ |
| 409 | char *transport_receive_line(UrlData *pUrlData){ |
| 410 | int i; |
| 411 | int iStart; |
| 412 | |
| 413 | i = iStart = transport.iCursor; |
| 414 | while(1){ |
| 415 | if( i >= transport.nUsed ){ |
| 416 | transport_load_buffer(pUrlData, pUrlData->isSsh ? 2 : 1000); |
| 417 | i -= iStart; |
| 418 | iStart = 0; |
| 419 | if( i >= transport.nUsed ){ |
| 420 | transport.pBuf[i] = 0; |
| 421 | transport.iCursor = i; |
| @@ -437,15 +437,15 @@ | |
| 437 | } |
| 438 | |
| 439 | /* |
| 440 | ** Global transport shutdown |
| 441 | */ |
| 442 | void transport_global_shutdown(UrlData *pUrlData){ |
| 443 | if( pUrlData->isSsh ){ |
| 444 | transport_ssh_close(); |
| 445 | } |
| 446 | if( pUrlData->isHttps ){ |
| 447 | #ifdef FOSSIL_ENABLE_SSL |
| 448 | ssl_global_shutdown(); |
| 449 | #endif |
| 450 | }else{ |
| 451 | socket_global_shutdown(); |
| 452 |
+6
-6
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -594,19 +594,19 @@ | ||
| 594 | 594 | if( (zPJ[jj]>0 && zPJ[jj]<' ') || strchr("\"*/:<>?\\|", zPJ[jj]) ){ |
| 595 | 595 | zPJ[jj] = '_'; |
| 596 | 596 | } |
| 597 | 597 | } |
| 598 | 598 | @ <tr><th>Timelines:</th><td> |
| 599 | - @ %z(href("%R/timeline?f=%S",zUuid))family</a> | |
| 599 | + @ %z(href("%R/timeline?f=%S&unhide",zUuid))family</a> | |
| 600 | 600 | if( zParent ){ |
| 601 | - @ | %z(href("%R/timeline?p=%S",zUuid))ancestors</a> | |
| 601 | + @ | %z(href("%R/timeline?p=%S&unhide",zUuid))ancestors</a> | |
| 602 | 602 | } |
| 603 | 603 | if( !isLeaf ){ |
| 604 | - @ | %z(href("%R/timeline?d=%S",zUuid))descendants</a> | |
| 604 | + @ | %z(href("%R/timeline?d=%S&unhide",zUuid))descendants</a> | |
| 605 | 605 | } |
| 606 | 606 | if( zParent && !isLeaf ){ |
| 607 | - @ | %z(href("%R/timeline?dp=%S",zUuid))both</a> | |
| 607 | + @ | %z(href("%R/timeline?dp=%S&unhide",zUuid))both</a> | |
| 608 | 608 | } |
| 609 | 609 | db_prepare(&q2,"SELECT substr(tag.tagname,5) FROM tagxref, tag " |
| 610 | 610 | " WHERE rid=%d AND tagtype>0 " |
| 611 | 611 | " AND tag.tagid=tagxref.tagid " |
| 612 | 612 | " AND +tag.tagname GLOB 'sym-*'", rid); |
| @@ -1634,11 +1634,11 @@ | ||
| 1634 | 1634 | objType = object_description(rid, 0, &downloadName); |
| 1635 | 1635 | style_submenu_element("Download", "Download", |
| 1636 | 1636 | "%R/raw/%T?name=%s", blob_str(&downloadName), zUuid); |
| 1637 | 1637 | if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){ |
| 1638 | 1638 | style_submenu_element("Checkins Using", "Checkins Using", |
| 1639 | - "%R/timeline?uf=%s&n=200",zUuid); | |
| 1639 | + "%R/timeline?n=200&uf=%s",zUuid); | |
| 1640 | 1640 | } |
| 1641 | 1641 | asText = P("txt")!=0; |
| 1642 | 1642 | zMime = mimetype_from_name(blob_str(&downloadName)); |
| 1643 | 1643 | if( zMime ){ |
| 1644 | 1644 | if( fossil_strcmp(zMime, "text/html")==0 ){ |
| @@ -2195,11 +2195,11 @@ | ||
| 2195 | 2195 | md5sum_blob(&ctrl, &cksum); |
| 2196 | 2196 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 2197 | 2197 | db_begin_transaction(); |
| 2198 | 2198 | g.markPrivate = content_is_private(rid); |
| 2199 | 2199 | nrid = content_put(&ctrl); |
| 2200 | - manifest_crosslink(nrid, &ctrl); | |
| 2200 | + manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); | |
| 2201 | 2201 | assert( blob_is_reset(&ctrl) ); |
| 2202 | 2202 | db_end_transaction(0); |
| 2203 | 2203 | } |
| 2204 | 2204 | cgi_redirectf("ci?name=%s", zUuid); |
| 2205 | 2205 | } |
| 2206 | 2206 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -594,19 +594,19 @@ | |
| 594 | if( (zPJ[jj]>0 && zPJ[jj]<' ') || strchr("\"*/:<>?\\|", zPJ[jj]) ){ |
| 595 | zPJ[jj] = '_'; |
| 596 | } |
| 597 | } |
| 598 | @ <tr><th>Timelines:</th><td> |
| 599 | @ %z(href("%R/timeline?f=%S",zUuid))family</a> |
| 600 | if( zParent ){ |
| 601 | @ | %z(href("%R/timeline?p=%S",zUuid))ancestors</a> |
| 602 | } |
| 603 | if( !isLeaf ){ |
| 604 | @ | %z(href("%R/timeline?d=%S",zUuid))descendants</a> |
| 605 | } |
| 606 | if( zParent && !isLeaf ){ |
| 607 | @ | %z(href("%R/timeline?dp=%S",zUuid))both</a> |
| 608 | } |
| 609 | db_prepare(&q2,"SELECT substr(tag.tagname,5) FROM tagxref, tag " |
| 610 | " WHERE rid=%d AND tagtype>0 " |
| 611 | " AND tag.tagid=tagxref.tagid " |
| 612 | " AND +tag.tagname GLOB 'sym-*'", rid); |
| @@ -1634,11 +1634,11 @@ | |
| 1634 | objType = object_description(rid, 0, &downloadName); |
| 1635 | style_submenu_element("Download", "Download", |
| 1636 | "%R/raw/%T?name=%s", blob_str(&downloadName), zUuid); |
| 1637 | if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){ |
| 1638 | style_submenu_element("Checkins Using", "Checkins Using", |
| 1639 | "%R/timeline?uf=%s&n=200",zUuid); |
| 1640 | } |
| 1641 | asText = P("txt")!=0; |
| 1642 | zMime = mimetype_from_name(blob_str(&downloadName)); |
| 1643 | if( zMime ){ |
| 1644 | if( fossil_strcmp(zMime, "text/html")==0 ){ |
| @@ -2195,11 +2195,11 @@ | |
| 2195 | md5sum_blob(&ctrl, &cksum); |
| 2196 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 2197 | db_begin_transaction(); |
| 2198 | g.markPrivate = content_is_private(rid); |
| 2199 | nrid = content_put(&ctrl); |
| 2200 | manifest_crosslink(nrid, &ctrl); |
| 2201 | assert( blob_is_reset(&ctrl) ); |
| 2202 | db_end_transaction(0); |
| 2203 | } |
| 2204 | cgi_redirectf("ci?name=%s", zUuid); |
| 2205 | } |
| 2206 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -594,19 +594,19 @@ | |
| 594 | if( (zPJ[jj]>0 && zPJ[jj]<' ') || strchr("\"*/:<>?\\|", zPJ[jj]) ){ |
| 595 | zPJ[jj] = '_'; |
| 596 | } |
| 597 | } |
| 598 | @ <tr><th>Timelines:</th><td> |
| 599 | @ %z(href("%R/timeline?f=%S&unhide",zUuid))family</a> |
| 600 | if( zParent ){ |
| 601 | @ | %z(href("%R/timeline?p=%S&unhide",zUuid))ancestors</a> |
| 602 | } |
| 603 | if( !isLeaf ){ |
| 604 | @ | %z(href("%R/timeline?d=%S&unhide",zUuid))descendants</a> |
| 605 | } |
| 606 | if( zParent && !isLeaf ){ |
| 607 | @ | %z(href("%R/timeline?dp=%S&unhide",zUuid))both</a> |
| 608 | } |
| 609 | db_prepare(&q2,"SELECT substr(tag.tagname,5) FROM tagxref, tag " |
| 610 | " WHERE rid=%d AND tagtype>0 " |
| 611 | " AND tag.tagid=tagxref.tagid " |
| 612 | " AND +tag.tagname GLOB 'sym-*'", rid); |
| @@ -1634,11 +1634,11 @@ | |
| 1634 | objType = object_description(rid, 0, &downloadName); |
| 1635 | style_submenu_element("Download", "Download", |
| 1636 | "%R/raw/%T?name=%s", blob_str(&downloadName), zUuid); |
| 1637 | if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){ |
| 1638 | style_submenu_element("Checkins Using", "Checkins Using", |
| 1639 | "%R/timeline?n=200&uf=%s",zUuid); |
| 1640 | } |
| 1641 | asText = P("txt")!=0; |
| 1642 | zMime = mimetype_from_name(blob_str(&downloadName)); |
| 1643 | if( zMime ){ |
| 1644 | if( fossil_strcmp(zMime, "text/html")==0 ){ |
| @@ -2195,11 +2195,11 @@ | |
| 2195 | md5sum_blob(&ctrl, &cksum); |
| 2196 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 2197 | db_begin_transaction(); |
| 2198 | g.markPrivate = content_is_private(rid); |
| 2199 | nrid = content_put(&ctrl); |
| 2200 | manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); |
| 2201 | assert( blob_is_reset(&ctrl) ); |
| 2202 | db_end_transaction(0); |
| 2203 | } |
| 2204 | cgi_redirectf("ci?name=%s", zUuid); |
| 2205 | } |
| 2206 |
+2
-2
| --- src/json_branch.c | ||
| +++ src/json_branch.c | ||
| @@ -291,12 +291,12 @@ | ||
| 291 | 291 | brid = content_put(&branch); |
| 292 | 292 | if( brid==0 ){ |
| 293 | 293 | fossil_fatal("Problem committing manifest: %s", g.zErrMsg); |
| 294 | 294 | } |
| 295 | 295 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 296 | - if( manifest_crosslink(brid, &branch)==0 ){ | |
| 297 | - fossil_fatal("unable to install new manifest"); | |
| 296 | + if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ | |
| 297 | + fossil_fatal("%s\n", g.zErrMsg); | |
| 298 | 298 | } |
| 299 | 299 | assert( blob_is_reset(&branch) ); |
| 300 | 300 | content_deltify(rootid, brid, 0); |
| 301 | 301 | if( zNewRid ){ |
| 302 | 302 | *zNewRid = brid; |
| 303 | 303 |
| --- src/json_branch.c | |
| +++ src/json_branch.c | |
| @@ -291,12 +291,12 @@ | |
| 291 | brid = content_put(&branch); |
| 292 | if( brid==0 ){ |
| 293 | fossil_fatal("Problem committing manifest: %s", g.zErrMsg); |
| 294 | } |
| 295 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 296 | if( manifest_crosslink(brid, &branch)==0 ){ |
| 297 | fossil_fatal("unable to install new manifest"); |
| 298 | } |
| 299 | assert( blob_is_reset(&branch) ); |
| 300 | content_deltify(rootid, brid, 0); |
| 301 | if( zNewRid ){ |
| 302 | *zNewRid = brid; |
| 303 |
| --- src/json_branch.c | |
| +++ src/json_branch.c | |
| @@ -291,12 +291,12 @@ | |
| 291 | brid = content_put(&branch); |
| 292 | if( brid==0 ){ |
| 293 | fossil_fatal("Problem committing manifest: %s", g.zErrMsg); |
| 294 | } |
| 295 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 296 | if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ |
| 297 | fossil_fatal("%s\n", g.zErrMsg); |
| 298 | } |
| 299 | assert( blob_is_reset(&branch) ); |
| 300 | content_deltify(rootid, brid, 0); |
| 301 | if( zNewRid ){ |
| 302 | *zNewRid = brid; |
| 303 |
+7
-2
| --- src/json_config.c | ||
| +++ src/json_config.c | ||
| @@ -53,16 +53,21 @@ | ||
| 53 | 53 | */ |
| 54 | 54 | static const struct JsonConfigProperty { |
| 55 | 55 | char const * name; |
| 56 | 56 | int groupMask; |
| 57 | 57 | } JsonConfigProperties[] = { |
| 58 | -{ "css", CONFIGSET_SKIN }, | |
| 58 | +{ "css", CONFIGSET_CSS }, | |
| 59 | 59 | { "header", CONFIGSET_SKIN }, |
| 60 | 60 | { "footer", CONFIGSET_SKIN }, |
| 61 | +{ "logo-mimetype", CONFIGSET_SKIN }, | |
| 62 | +{ "logo-image", CONFIGSET_SKIN }, | |
| 63 | +{ "background-mimetype", CONFIGSET_SKIN }, | |
| 64 | +{ "background-image", CONFIGSET_SKIN }, | |
| 61 | 65 | { "index-page", CONFIGSET_SKIN }, |
| 62 | 66 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 63 | 67 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 68 | +{ "timeline-plaintext", CONFIGSET_SKIN }, | |
| 64 | 69 | |
| 65 | 70 | { "project-name", CONFIGSET_PROJ }, |
| 66 | 71 | { "project-description", CONFIGSET_PROJ }, |
| 67 | 72 | { "manifest", CONFIGSET_PROJ }, |
| 68 | 73 | { "binary-glob", CONFIGSET_PROJ }, |
| @@ -113,11 +118,11 @@ | ||
| 113 | 118 | if(0==(strcmp("all", zName))){ |
| 114 | 119 | confMask = CONFIGSET_ALL; |
| 115 | 120 | }else if(0==(strcmp("project", zName))){ |
| 116 | 121 | confMask |= CONFIGSET_PROJ; |
| 117 | 122 | }else if(0==(strcmp("skin", zName))){ |
| 118 | - confMask |= CONFIGSET_SKIN; | |
| 123 | + confMask |= (CONFIGSET_CSS|CONFIGSET_SKIN); | |
| 119 | 124 | }else if(0==(strcmp("ticket", zName))){ |
| 120 | 125 | confMask |= CONFIGSET_TKT; |
| 121 | 126 | }else if(0==(strcmp("skin-backup", zName))){ |
| 122 | 127 | optSkinBackups = 1; |
| 123 | 128 | }else{ |
| 124 | 129 |
| --- src/json_config.c | |
| +++ src/json_config.c | |
| @@ -53,16 +53,21 @@ | |
| 53 | */ |
| 54 | static const struct JsonConfigProperty { |
| 55 | char const * name; |
| 56 | int groupMask; |
| 57 | } JsonConfigProperties[] = { |
| 58 | { "css", CONFIGSET_SKIN }, |
| 59 | { "header", CONFIGSET_SKIN }, |
| 60 | { "footer", CONFIGSET_SKIN }, |
| 61 | { "index-page", CONFIGSET_SKIN }, |
| 62 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 63 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 64 | |
| 65 | { "project-name", CONFIGSET_PROJ }, |
| 66 | { "project-description", CONFIGSET_PROJ }, |
| 67 | { "manifest", CONFIGSET_PROJ }, |
| 68 | { "binary-glob", CONFIGSET_PROJ }, |
| @@ -113,11 +118,11 @@ | |
| 113 | if(0==(strcmp("all", zName))){ |
| 114 | confMask = CONFIGSET_ALL; |
| 115 | }else if(0==(strcmp("project", zName))){ |
| 116 | confMask |= CONFIGSET_PROJ; |
| 117 | }else if(0==(strcmp("skin", zName))){ |
| 118 | confMask |= CONFIGSET_SKIN; |
| 119 | }else if(0==(strcmp("ticket", zName))){ |
| 120 | confMask |= CONFIGSET_TKT; |
| 121 | }else if(0==(strcmp("skin-backup", zName))){ |
| 122 | optSkinBackups = 1; |
| 123 | }else{ |
| 124 |
| --- src/json_config.c | |
| +++ src/json_config.c | |
| @@ -53,16 +53,21 @@ | |
| 53 | */ |
| 54 | static const struct JsonConfigProperty { |
| 55 | char const * name; |
| 56 | int groupMask; |
| 57 | } JsonConfigProperties[] = { |
| 58 | { "css", CONFIGSET_CSS }, |
| 59 | { "header", CONFIGSET_SKIN }, |
| 60 | { "footer", CONFIGSET_SKIN }, |
| 61 | { "logo-mimetype", CONFIGSET_SKIN }, |
| 62 | { "logo-image", CONFIGSET_SKIN }, |
| 63 | { "background-mimetype", CONFIGSET_SKIN }, |
| 64 | { "background-image", CONFIGSET_SKIN }, |
| 65 | { "index-page", CONFIGSET_SKIN }, |
| 66 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 67 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 68 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 69 | |
| 70 | { "project-name", CONFIGSET_PROJ }, |
| 71 | { "project-description", CONFIGSET_PROJ }, |
| 72 | { "manifest", CONFIGSET_PROJ }, |
| 73 | { "binary-glob", CONFIGSET_PROJ }, |
| @@ -113,11 +118,11 @@ | |
| 118 | if(0==(strcmp("all", zName))){ |
| 119 | confMask = CONFIGSET_ALL; |
| 120 | }else if(0==(strcmp("project", zName))){ |
| 121 | confMask |= CONFIGSET_PROJ; |
| 122 | }else if(0==(strcmp("skin", zName))){ |
| 123 | confMask |= (CONFIGSET_CSS|CONFIGSET_SKIN); |
| 124 | }else if(0==(strcmp("ticket", zName))){ |
| 125 | confMask |= CONFIGSET_TKT; |
| 126 | }else if(0==(strcmp("skin-backup", zName))){ |
| 127 | optSkinBackups = 1; |
| 128 | }else{ |
| 129 |
+16
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -114,10 +114,12 @@ | ||
| 114 | 114 | #endif |
| 115 | 115 | |
| 116 | 116 | /* |
| 117 | 117 | ** All global variables are in this structure. |
| 118 | 118 | */ |
| 119 | +#define GLOBAL_URL() ((UrlData *)(&g.urlIsFile)) | |
| 120 | + | |
| 119 | 121 | struct Global { |
| 120 | 122 | int argc; char **argv; /* Command-line arguments to the program */ |
| 121 | 123 | char *nameOfExe; /* Full path of executable. */ |
| 122 | 124 | const char *zErrlog; /* Log errors to this file, if not NULL */ |
| 123 | 125 | int isConst; /* True if the output is unchanging */ |
| @@ -168,10 +170,14 @@ | ||
| 168 | 170 | int wikiFlags; /* Wiki conversion flags applied to %w and %W */ |
| 169 | 171 | char isHTTP; /* True if server/CGI modes, else assume CLI. */ |
| 170 | 172 | char javascriptHyperlink; /* If true, set href= using script, not HTML */ |
| 171 | 173 | Blob httpHeader; /* Complete text of the HTTP request header */ |
| 172 | 174 | |
| 175 | + /* | |
| 176 | + ** NOTE: These members MUST be kept in sync with those in the "UrlData" | |
| 177 | + ** structure defined in "url.c". | |
| 178 | + */ | |
| 173 | 179 | int urlIsFile; /* True if a "file:" url */ |
| 174 | 180 | int urlIsHttps; /* True if a "https:" url */ |
| 175 | 181 | int urlIsSsh; /* True if an "ssh:" url */ |
| 176 | 182 | char *urlName; /* Hostname for http: or filename for file: */ |
| 177 | 183 | char *urlHostname; /* The HOST: parameter on http headers */ |
| @@ -831,10 +837,20 @@ | ||
| 831 | 837 | const char *get_version(){ |
| 832 | 838 | static const char version[] = RELEASE_VERSION " " MANIFEST_VERSION " " |
| 833 | 839 | MANIFEST_DATE " UTC"; |
| 834 | 840 | return version; |
| 835 | 841 | } |
| 842 | + | |
| 843 | +/* | |
| 844 | +** This function returns the user-agent string for Fossil, for | |
| 845 | +** use in HTTP(S) requests. | |
| 846 | +*/ | |
| 847 | +const char *get_user_agent(){ | |
| 848 | + static const char version[] = "Fossil/" RELEASE_VERSION " (" MANIFEST_DATE | |
| 849 | + " " MANIFEST_VERSION ")"; | |
| 850 | + return version; | |
| 851 | +} | |
| 836 | 852 | |
| 837 | 853 | /* |
| 838 | 854 | ** COMMAND: version |
| 839 | 855 | ** |
| 840 | 856 | ** Usage: %fossil version ?-verbose|-v? |
| 841 | 857 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -114,10 +114,12 @@ | |
| 114 | #endif |
| 115 | |
| 116 | /* |
| 117 | ** All global variables are in this structure. |
| 118 | */ |
| 119 | struct Global { |
| 120 | int argc; char **argv; /* Command-line arguments to the program */ |
| 121 | char *nameOfExe; /* Full path of executable. */ |
| 122 | const char *zErrlog; /* Log errors to this file, if not NULL */ |
| 123 | int isConst; /* True if the output is unchanging */ |
| @@ -168,10 +170,14 @@ | |
| 168 | int wikiFlags; /* Wiki conversion flags applied to %w and %W */ |
| 169 | char isHTTP; /* True if server/CGI modes, else assume CLI. */ |
| 170 | char javascriptHyperlink; /* If true, set href= using script, not HTML */ |
| 171 | Blob httpHeader; /* Complete text of the HTTP request header */ |
| 172 | |
| 173 | int urlIsFile; /* True if a "file:" url */ |
| 174 | int urlIsHttps; /* True if a "https:" url */ |
| 175 | int urlIsSsh; /* True if an "ssh:" url */ |
| 176 | char *urlName; /* Hostname for http: or filename for file: */ |
| 177 | char *urlHostname; /* The HOST: parameter on http headers */ |
| @@ -831,10 +837,20 @@ | |
| 831 | const char *get_version(){ |
| 832 | static const char version[] = RELEASE_VERSION " " MANIFEST_VERSION " " |
| 833 | MANIFEST_DATE " UTC"; |
| 834 | return version; |
| 835 | } |
| 836 | |
| 837 | /* |
| 838 | ** COMMAND: version |
| 839 | ** |
| 840 | ** Usage: %fossil version ?-verbose|-v? |
| 841 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -114,10 +114,12 @@ | |
| 114 | #endif |
| 115 | |
| 116 | /* |
| 117 | ** All global variables are in this structure. |
| 118 | */ |
| 119 | #define GLOBAL_URL() ((UrlData *)(&g.urlIsFile)) |
| 120 | |
| 121 | struct Global { |
| 122 | int argc; char **argv; /* Command-line arguments to the program */ |
| 123 | char *nameOfExe; /* Full path of executable. */ |
| 124 | const char *zErrlog; /* Log errors to this file, if not NULL */ |
| 125 | int isConst; /* True if the output is unchanging */ |
| @@ -168,10 +170,14 @@ | |
| 170 | int wikiFlags; /* Wiki conversion flags applied to %w and %W */ |
| 171 | char isHTTP; /* True if server/CGI modes, else assume CLI. */ |
| 172 | char javascriptHyperlink; /* If true, set href= using script, not HTML */ |
| 173 | Blob httpHeader; /* Complete text of the HTTP request header */ |
| 174 | |
| 175 | /* |
| 176 | ** NOTE: These members MUST be kept in sync with those in the "UrlData" |
| 177 | ** structure defined in "url.c". |
| 178 | */ |
| 179 | int urlIsFile; /* True if a "file:" url */ |
| 180 | int urlIsHttps; /* True if a "https:" url */ |
| 181 | int urlIsSsh; /* True if an "ssh:" url */ |
| 182 | char *urlName; /* Hostname for http: or filename for file: */ |
| 183 | char *urlHostname; /* The HOST: parameter on http headers */ |
| @@ -831,10 +837,20 @@ | |
| 837 | const char *get_version(){ |
| 838 | static const char version[] = RELEASE_VERSION " " MANIFEST_VERSION " " |
| 839 | MANIFEST_DATE " UTC"; |
| 840 | return version; |
| 841 | } |
| 842 | |
| 843 | /* |
| 844 | ** This function returns the user-agent string for Fossil, for |
| 845 | ** use in HTTP(S) requests. |
| 846 | */ |
| 847 | const char *get_user_agent(){ |
| 848 | static const char version[] = "Fossil/" RELEASE_VERSION " (" MANIFEST_DATE |
| 849 | " " MANIFEST_VERSION ")"; |
| 850 | return version; |
| 851 | } |
| 852 | |
| 853 | /* |
| 854 | ** COMMAND: version |
| 855 | ** |
| 856 | ** Usage: %fossil version ?-verbose|-v? |
| 857 |
+16
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -114,10 +114,12 @@ | ||
| 114 | 114 | #endif |
| 115 | 115 | |
| 116 | 116 | /* |
| 117 | 117 | ** All global variables are in this structure. |
| 118 | 118 | */ |
| 119 | +#define GLOBAL_URL() ((UrlData *)(&g.urlIsFile)) | |
| 120 | + | |
| 119 | 121 | struct Global { |
| 120 | 122 | int argc; char **argv; /* Command-line arguments to the program */ |
| 121 | 123 | char *nameOfExe; /* Full path of executable. */ |
| 122 | 124 | const char *zErrlog; /* Log errors to this file, if not NULL */ |
| 123 | 125 | int isConst; /* True if the output is unchanging */ |
| @@ -168,10 +170,14 @@ | ||
| 168 | 170 | int wikiFlags; /* Wiki conversion flags applied to %w and %W */ |
| 169 | 171 | char isHTTP; /* True if server/CGI modes, else assume CLI. */ |
| 170 | 172 | char javascriptHyperlink; /* If true, set href= using script, not HTML */ |
| 171 | 173 | Blob httpHeader; /* Complete text of the HTTP request header */ |
| 172 | 174 | |
| 175 | + /* | |
| 176 | + ** NOTE: These members MUST be kept in sync with those in the "UrlData" | |
| 177 | + ** structure defined in "url.c". | |
| 178 | + */ | |
| 173 | 179 | int urlIsFile; /* True if a "file:" url */ |
| 174 | 180 | int urlIsHttps; /* True if a "https:" url */ |
| 175 | 181 | int urlIsSsh; /* True if an "ssh:" url */ |
| 176 | 182 | char *urlName; /* Hostname for http: or filename for file: */ |
| 177 | 183 | char *urlHostname; /* The HOST: parameter on http headers */ |
| @@ -831,10 +837,20 @@ | ||
| 831 | 837 | const char *get_version(){ |
| 832 | 838 | static const char version[] = RELEASE_VERSION " " MANIFEST_VERSION " " |
| 833 | 839 | MANIFEST_DATE " UTC"; |
| 834 | 840 | return version; |
| 835 | 841 | } |
| 842 | + | |
| 843 | +/* | |
| 844 | +** This function returns the user-agent string for Fossil, for | |
| 845 | +** use in HTTP(S) requests. | |
| 846 | +*/ | |
| 847 | +const char *get_user_agent(){ | |
| 848 | + static const char version[] = "Fossil/" RELEASE_VERSION " (" MANIFEST_DATE | |
| 849 | + " " MANIFEST_VERSION ")"; | |
| 850 | + return version; | |
| 851 | +} | |
| 836 | 852 | |
| 837 | 853 | /* |
| 838 | 854 | ** COMMAND: version |
| 839 | 855 | ** |
| 840 | 856 | ** Usage: %fossil version ?-verbose|-v? |
| 841 | 857 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -114,10 +114,12 @@ | |
| 114 | #endif |
| 115 | |
| 116 | /* |
| 117 | ** All global variables are in this structure. |
| 118 | */ |
| 119 | struct Global { |
| 120 | int argc; char **argv; /* Command-line arguments to the program */ |
| 121 | char *nameOfExe; /* Full path of executable. */ |
| 122 | const char *zErrlog; /* Log errors to this file, if not NULL */ |
| 123 | int isConst; /* True if the output is unchanging */ |
| @@ -168,10 +170,14 @@ | |
| 168 | int wikiFlags; /* Wiki conversion flags applied to %w and %W */ |
| 169 | char isHTTP; /* True if server/CGI modes, else assume CLI. */ |
| 170 | char javascriptHyperlink; /* If true, set href= using script, not HTML */ |
| 171 | Blob httpHeader; /* Complete text of the HTTP request header */ |
| 172 | |
| 173 | int urlIsFile; /* True if a "file:" url */ |
| 174 | int urlIsHttps; /* True if a "https:" url */ |
| 175 | int urlIsSsh; /* True if an "ssh:" url */ |
| 176 | char *urlName; /* Hostname for http: or filename for file: */ |
| 177 | char *urlHostname; /* The HOST: parameter on http headers */ |
| @@ -831,10 +837,20 @@ | |
| 831 | const char *get_version(){ |
| 832 | static const char version[] = RELEASE_VERSION " " MANIFEST_VERSION " " |
| 833 | MANIFEST_DATE " UTC"; |
| 834 | return version; |
| 835 | } |
| 836 | |
| 837 | /* |
| 838 | ** COMMAND: version |
| 839 | ** |
| 840 | ** Usage: %fossil version ?-verbose|-v? |
| 841 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -114,10 +114,12 @@ | |
| 114 | #endif |
| 115 | |
| 116 | /* |
| 117 | ** All global variables are in this structure. |
| 118 | */ |
| 119 | #define GLOBAL_URL() ((UrlData *)(&g.urlIsFile)) |
| 120 | |
| 121 | struct Global { |
| 122 | int argc; char **argv; /* Command-line arguments to the program */ |
| 123 | char *nameOfExe; /* Full path of executable. */ |
| 124 | const char *zErrlog; /* Log errors to this file, if not NULL */ |
| 125 | int isConst; /* True if the output is unchanging */ |
| @@ -168,10 +170,14 @@ | |
| 170 | int wikiFlags; /* Wiki conversion flags applied to %w and %W */ |
| 171 | char isHTTP; /* True if server/CGI modes, else assume CLI. */ |
| 172 | char javascriptHyperlink; /* If true, set href= using script, not HTML */ |
| 173 | Blob httpHeader; /* Complete text of the HTTP request header */ |
| 174 | |
| 175 | /* |
| 176 | ** NOTE: These members MUST be kept in sync with those in the "UrlData" |
| 177 | ** structure defined in "url.c". |
| 178 | */ |
| 179 | int urlIsFile; /* True if a "file:" url */ |
| 180 | int urlIsHttps; /* True if a "https:" url */ |
| 181 | int urlIsSsh; /* True if an "ssh:" url */ |
| 182 | char *urlName; /* Hostname for http: or filename for file: */ |
| 183 | char *urlHostname; /* The HOST: parameter on http headers */ |
| @@ -831,10 +837,20 @@ | |
| 837 | const char *get_version(){ |
| 838 | static const char version[] = RELEASE_VERSION " " MANIFEST_VERSION " " |
| 839 | MANIFEST_DATE " UTC"; |
| 840 | return version; |
| 841 | } |
| 842 | |
| 843 | /* |
| 844 | ** This function returns the user-agent string for Fossil, for |
| 845 | ** use in HTTP(S) requests. |
| 846 | */ |
| 847 | const char *get_user_agent(){ |
| 848 | static const char version[] = "Fossil/" RELEASE_VERSION " (" MANIFEST_DATE |
| 849 | " " MANIFEST_VERSION ")"; |
| 850 | return version; |
| 851 | } |
| 852 | |
| 853 | /* |
| 854 | ** COMMAND: version |
| 855 | ** |
| 856 | ** Usage: %fossil version ?-verbose|-v? |
| 857 |
+39
-12
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -42,10 +42,16 @@ | ||
| 42 | 42 | */ |
| 43 | 43 | #define PERM_REG 0 /* regular file */ |
| 44 | 44 | #define PERM_EXE 1 /* executable */ |
| 45 | 45 | #define PERM_LNK 2 /* symlink */ |
| 46 | 46 | |
| 47 | +/* | |
| 48 | +** Flags for use with manifest_crosslink(). | |
| 49 | +*/ | |
| 50 | +#define MC_NONE 0 /* default handling */ | |
| 51 | +#define MC_PERMIT_HOOKS 1 /* permit hooks to execute */ | |
| 52 | + | |
| 47 | 53 | /* |
| 48 | 54 | ** A single F-card within a manifest |
| 49 | 55 | */ |
| 50 | 56 | struct ManifestFile { |
| 51 | 57 | char *zName; /* Name of a file */ |
| @@ -1650,34 +1656,41 @@ | ||
| 1650 | 1656 | ** Historical note: This routine original processed manifests only. |
| 1651 | 1657 | ** Processing for other control artifacts was added later. The name |
| 1652 | 1658 | ** of the routine, "manifest_crosslink", and the name of this source |
| 1653 | 1659 | ** file, is a legacy of its original use. |
| 1654 | 1660 | */ |
| 1655 | -int manifest_crosslink(int rid, Blob *pContent){ | |
| 1656 | - int i; | |
| 1661 | +int manifest_crosslink(int rid, Blob *pContent, int flags){ | |
| 1662 | + int i, result = TH_OK; | |
| 1657 | 1663 | Manifest *p; |
| 1658 | 1664 | Stmt q; |
| 1659 | 1665 | int parentid = 0; |
| 1666 | + const char *zScript = 0; | |
| 1667 | + const char *zUuid = 0; | |
| 1660 | 1668 | |
| 1661 | 1669 | if( (p = manifest_cache_find(rid))!=0 ){ |
| 1662 | 1670 | blob_reset(pContent); |
| 1663 | 1671 | }else if( (p = manifest_parse(pContent, rid, 0))==0 ){ |
| 1664 | 1672 | assert( blob_is_reset(pContent) || pContent==0 ); |
| 1673 | + fossil_error(1, "syntax error in manifest"); | |
| 1665 | 1674 | return 0; |
| 1666 | 1675 | } |
| 1667 | 1676 | if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){ |
| 1668 | 1677 | manifest_destroy(p); |
| 1669 | 1678 | assert( blob_is_reset(pContent) ); |
| 1679 | + fossil_error(1, "no manifest"); | |
| 1670 | 1680 | return 0; |
| 1671 | 1681 | } |
| 1672 | 1682 | if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){ |
| 1673 | 1683 | manifest_destroy(p); |
| 1674 | 1684 | assert( blob_is_reset(pContent) ); |
| 1685 | + fossil_error(1, "cannot fetch baseline manifest"); | |
| 1675 | 1686 | return 0; |
| 1676 | 1687 | } |
| 1677 | 1688 | db_begin_transaction(); |
| 1678 | 1689 | if( p->type==CFTYPE_MANIFEST ){ |
| 1690 | + zScript = xfer_commit_code(); | |
| 1691 | + zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 1679 | 1692 | if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ |
| 1680 | 1693 | char *zCom; |
| 1681 | 1694 | for(i=0; i<p->nParent; i++){ |
| 1682 | 1695 | int pid = uuid_to_rid(p->azParent[i], 1); |
| 1683 | 1696 | db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" |
| @@ -1768,11 +1781,11 @@ | ||
| 1768 | 1781 | switch( p->aTag[i].zName[0] ){ |
| 1769 | 1782 | case '-': type = 0; break; /* Cancel prior occurrences */ |
| 1770 | 1783 | case '+': type = 1; break; /* Apply to target only */ |
| 1771 | 1784 | case '*': type = 2; break; /* Propagate to descendants */ |
| 1772 | 1785 | default: |
| 1773 | - fossil_fatal("unknown tag type in manifest: %s", p->aTag); | |
| 1786 | + fossil_error(1, "unknown tag type in manifest: %s", p->aTag); | |
| 1774 | 1787 | return 0; |
| 1775 | 1788 | } |
| 1776 | 1789 | tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, |
| 1777 | 1790 | rid, p->rDate, tid); |
| 1778 | 1791 | } |
| @@ -1870,10 +1883,12 @@ | ||
| 1870 | 1883 | } |
| 1871 | 1884 | } |
| 1872 | 1885 | if( p->type==CFTYPE_TICKET ){ |
| 1873 | 1886 | char *zTag; |
| 1874 | 1887 | |
| 1888 | + zScript = xfer_ticket_code(); | |
| 1889 | + zUuid = p->zTicketUuid; | |
| 1875 | 1890 | assert( manifest_crosslink_busy==1 ); |
| 1876 | 1891 | zTag = mprintf("tkt-%s", p->zTicketUuid); |
| 1877 | 1892 | tag_insert(zTag, 1, 0, rid, p->rDate, rid); |
| 1878 | 1893 | free(zTag); |
| 1879 | 1894 | db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", |
| @@ -1934,33 +1949,39 @@ | ||
| 1934 | 1949 | if( p->type==CFTYPE_CONTROL ){ |
| 1935 | 1950 | Blob comment; |
| 1936 | 1951 | int i; |
| 1937 | 1952 | const char *zName; |
| 1938 | 1953 | const char *zValue; |
| 1939 | - const char *zUuid; | |
| 1954 | + const char *zTagUuid; | |
| 1940 | 1955 | int branchMove = 0; |
| 1941 | 1956 | blob_zero(&comment); |
| 1942 | 1957 | if( p->zComment ){ |
| 1943 | 1958 | blob_appendf(&comment, " %s.", p->zComment); |
| 1944 | 1959 | } |
| 1945 | 1960 | /* Next loop expects tags to be sorted on UUID, so sort it. */ |
| 1946 | 1961 | qsort(p->aTag, p->nTag, sizeof(p->aTag[0]), tag_compare); |
| 1947 | 1962 | for(i=0; i<p->nTag; i++){ |
| 1948 | - zUuid = p->aTag[i].zUuid; | |
| 1949 | - if( !zUuid ) continue; | |
| 1950 | - if( i==0 || fossil_strcmp(zUuid, p->aTag[i-1].zUuid)!=0 ){ | |
| 1963 | + zTagUuid = p->aTag[i].zUuid; | |
| 1964 | + if( !zTagUuid ) continue; | |
| 1965 | + if( i==0 || fossil_strcmp(zTagUuid, p->aTag[i-1].zUuid)!=0 ){ | |
| 1951 | 1966 | blob_appendf(&comment, |
| 1952 | 1967 | " Edit [%S]:", |
| 1953 | - zUuid); | |
| 1968 | + zTagUuid); | |
| 1954 | 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 | + } | |
| 1955 | 1976 | } |
| 1956 | 1977 | zName = p->aTag[i].zName; |
| 1957 | 1978 | zValue = p->aTag[i].zValue; |
| 1958 | 1979 | if( strcmp(zName, "*branch")==0 ){ |
| 1959 | 1980 | blob_appendf(&comment, |
| 1960 | - " Move to branch [/timeline?r=%h&nd&dp=%S | %h].", | |
| 1961 | - zValue, zUuid, zValue); | |
| 1981 | + " Move to branch [/timeline?r=%h&nd&dp=%S&unhide | %h].", | |
| 1982 | + zValue, zTagUuid, zValue); | |
| 1962 | 1983 | branchMove = 1; |
| 1963 | 1984 | continue; |
| 1964 | 1985 | }else if( strcmp(zName, "*bgcolor")==0 ){ |
| 1965 | 1986 | blob_appendf(&comment, |
| 1966 | 1987 | " Change branch background color to \"%h\".", zValue); |
| @@ -2021,17 +2042,23 @@ | ||
| 2021 | 2042 | p->rDate, rid, p->zUser, blob_str(&comment)+1 |
| 2022 | 2043 | ); |
| 2023 | 2044 | blob_reset(&comment); |
| 2024 | 2045 | } |
| 2025 | 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 | + } | |
| 2026 | 2053 | if( p->type==CFTYPE_MANIFEST ){ |
| 2027 | 2054 | manifest_cache_insert(p); |
| 2028 | 2055 | }else{ |
| 2029 | 2056 | manifest_destroy(p); |
| 2030 | 2057 | } |
| 2031 | 2058 | assert( blob_is_reset(pContent) ); |
| 2032 | - return 1; | |
| 2059 | + return ( result!=TH_ERROR ); | |
| 2033 | 2060 | } |
| 2034 | 2061 | |
| 2035 | 2062 | /* |
| 2036 | 2063 | ** COMMAND: test-crosslink |
| 2037 | 2064 | ** |
| @@ -2045,7 +2072,7 @@ | ||
| 2045 | 2072 | Blob content; |
| 2046 | 2073 | db_find_and_open_repository(0, 0); |
| 2047 | 2074 | if( g.argc!=3 ) usage("RECORDID"); |
| 2048 | 2075 | rid = name_to_rid(g.argv[2]); |
| 2049 | 2076 | content_get(rid, &content); |
| 2050 | - manifest_crosslink(rid, &content); | |
| 2077 | + manifest_crosslink(rid, &content, MC_NONE); | |
| 2051 | 2078 | } |
| 2052 | 2079 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -42,10 +42,16 @@ | |
| 42 | */ |
| 43 | #define PERM_REG 0 /* regular file */ |
| 44 | #define PERM_EXE 1 /* executable */ |
| 45 | #define PERM_LNK 2 /* symlink */ |
| 46 | |
| 47 | /* |
| 48 | ** A single F-card within a manifest |
| 49 | */ |
| 50 | struct ManifestFile { |
| 51 | char *zName; /* Name of a file */ |
| @@ -1650,34 +1656,41 @@ | |
| 1650 | ** Historical note: This routine original processed manifests only. |
| 1651 | ** Processing for other control artifacts was added later. The name |
| 1652 | ** of the routine, "manifest_crosslink", and the name of this source |
| 1653 | ** file, is a legacy of its original use. |
| 1654 | */ |
| 1655 | int manifest_crosslink(int rid, Blob *pContent){ |
| 1656 | int i; |
| 1657 | Manifest *p; |
| 1658 | Stmt q; |
| 1659 | int parentid = 0; |
| 1660 | |
| 1661 | if( (p = manifest_cache_find(rid))!=0 ){ |
| 1662 | blob_reset(pContent); |
| 1663 | }else if( (p = manifest_parse(pContent, rid, 0))==0 ){ |
| 1664 | assert( blob_is_reset(pContent) || pContent==0 ); |
| 1665 | return 0; |
| 1666 | } |
| 1667 | if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){ |
| 1668 | manifest_destroy(p); |
| 1669 | assert( blob_is_reset(pContent) ); |
| 1670 | return 0; |
| 1671 | } |
| 1672 | if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){ |
| 1673 | manifest_destroy(p); |
| 1674 | assert( blob_is_reset(pContent) ); |
| 1675 | return 0; |
| 1676 | } |
| 1677 | db_begin_transaction(); |
| 1678 | if( p->type==CFTYPE_MANIFEST ){ |
| 1679 | if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ |
| 1680 | char *zCom; |
| 1681 | for(i=0; i<p->nParent; i++){ |
| 1682 | int pid = uuid_to_rid(p->azParent[i], 1); |
| 1683 | db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" |
| @@ -1768,11 +1781,11 @@ | |
| 1768 | switch( p->aTag[i].zName[0] ){ |
| 1769 | case '-': type = 0; break; /* Cancel prior occurrences */ |
| 1770 | case '+': type = 1; break; /* Apply to target only */ |
| 1771 | case '*': type = 2; break; /* Propagate to descendants */ |
| 1772 | default: |
| 1773 | fossil_fatal("unknown tag type in manifest: %s", p->aTag); |
| 1774 | return 0; |
| 1775 | } |
| 1776 | tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, |
| 1777 | rid, p->rDate, tid); |
| 1778 | } |
| @@ -1870,10 +1883,12 @@ | |
| 1870 | } |
| 1871 | } |
| 1872 | if( p->type==CFTYPE_TICKET ){ |
| 1873 | char *zTag; |
| 1874 | |
| 1875 | assert( manifest_crosslink_busy==1 ); |
| 1876 | zTag = mprintf("tkt-%s", p->zTicketUuid); |
| 1877 | tag_insert(zTag, 1, 0, rid, p->rDate, rid); |
| 1878 | free(zTag); |
| 1879 | db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", |
| @@ -1934,33 +1949,39 @@ | |
| 1934 | if( p->type==CFTYPE_CONTROL ){ |
| 1935 | Blob comment; |
| 1936 | int i; |
| 1937 | const char *zName; |
| 1938 | const char *zValue; |
| 1939 | const char *zUuid; |
| 1940 | int branchMove = 0; |
| 1941 | blob_zero(&comment); |
| 1942 | if( p->zComment ){ |
| 1943 | blob_appendf(&comment, " %s.", p->zComment); |
| 1944 | } |
| 1945 | /* Next loop expects tags to be sorted on UUID, so sort it. */ |
| 1946 | qsort(p->aTag, p->nTag, sizeof(p->aTag[0]), tag_compare); |
| 1947 | for(i=0; i<p->nTag; i++){ |
| 1948 | zUuid = p->aTag[i].zUuid; |
| 1949 | if( !zUuid ) continue; |
| 1950 | if( i==0 || fossil_strcmp(zUuid, p->aTag[i-1].zUuid)!=0 ){ |
| 1951 | blob_appendf(&comment, |
| 1952 | " Edit [%S]:", |
| 1953 | zUuid); |
| 1954 | branchMove = 0; |
| 1955 | } |
| 1956 | zName = p->aTag[i].zName; |
| 1957 | zValue = p->aTag[i].zValue; |
| 1958 | if( strcmp(zName, "*branch")==0 ){ |
| 1959 | blob_appendf(&comment, |
| 1960 | " Move to branch [/timeline?r=%h&nd&dp=%S | %h].", |
| 1961 | zValue, zUuid, zValue); |
| 1962 | branchMove = 1; |
| 1963 | continue; |
| 1964 | }else if( strcmp(zName, "*bgcolor")==0 ){ |
| 1965 | blob_appendf(&comment, |
| 1966 | " Change branch background color to \"%h\".", zValue); |
| @@ -2021,17 +2042,23 @@ | |
| 2021 | p->rDate, rid, p->zUser, blob_str(&comment)+1 |
| 2022 | ); |
| 2023 | blob_reset(&comment); |
| 2024 | } |
| 2025 | db_end_transaction(0); |
| 2026 | if( p->type==CFTYPE_MANIFEST ){ |
| 2027 | manifest_cache_insert(p); |
| 2028 | }else{ |
| 2029 | manifest_destroy(p); |
| 2030 | } |
| 2031 | assert( blob_is_reset(pContent) ); |
| 2032 | return 1; |
| 2033 | } |
| 2034 | |
| 2035 | /* |
| 2036 | ** COMMAND: test-crosslink |
| 2037 | ** |
| @@ -2045,7 +2072,7 @@ | |
| 2045 | Blob content; |
| 2046 | db_find_and_open_repository(0, 0); |
| 2047 | if( g.argc!=3 ) usage("RECORDID"); |
| 2048 | rid = name_to_rid(g.argv[2]); |
| 2049 | content_get(rid, &content); |
| 2050 | manifest_crosslink(rid, &content); |
| 2051 | } |
| 2052 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -42,10 +42,16 @@ | |
| 42 | */ |
| 43 | #define PERM_REG 0 /* regular file */ |
| 44 | #define PERM_EXE 1 /* executable */ |
| 45 | #define PERM_LNK 2 /* symlink */ |
| 46 | |
| 47 | /* |
| 48 | ** Flags for use with manifest_crosslink(). |
| 49 | */ |
| 50 | #define MC_NONE 0 /* default handling */ |
| 51 | #define MC_PERMIT_HOOKS 1 /* permit hooks to execute */ |
| 52 | |
| 53 | /* |
| 54 | ** A single F-card within a manifest |
| 55 | */ |
| 56 | struct ManifestFile { |
| 57 | char *zName; /* Name of a file */ |
| @@ -1650,34 +1656,41 @@ | |
| 1656 | ** Historical note: This routine original processed manifests only. |
| 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); |
| 1671 | }else if( (p = manifest_parse(pContent, rid, 0))==0 ){ |
| 1672 | assert( blob_is_reset(pContent) || pContent==0 ); |
| 1673 | fossil_error(1, "syntax error in manifest"); |
| 1674 | return 0; |
| 1675 | } |
| 1676 | if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){ |
| 1677 | manifest_destroy(p); |
| 1678 | assert( blob_is_reset(pContent) ); |
| 1679 | fossil_error(1, "no manifest"); |
| 1680 | return 0; |
| 1681 | } |
| 1682 | if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){ |
| 1683 | manifest_destroy(p); |
| 1684 | assert( blob_is_reset(pContent) ); |
| 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); |
| 1696 | db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" |
| @@ -1768,11 +1781,11 @@ | |
| 1781 | switch( p->aTag[i].zName[0] ){ |
| 1782 | case '-': type = 0; break; /* Cancel prior occurrences */ |
| 1783 | case '+': type = 1; break; /* Apply to target only */ |
| 1784 | case '*': type = 2; break; /* Propagate to descendants */ |
| 1785 | default: |
| 1786 | fossil_error(1, "unknown tag type in manifest: %s", p->aTag); |
| 1787 | return 0; |
| 1788 | } |
| 1789 | tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, |
| 1790 | rid, p->rDate, tid); |
| 1791 | } |
| @@ -1870,10 +1883,12 @@ | |
| 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)", |
| @@ -1934,33 +1949,39 @@ | |
| 1949 | if( p->type==CFTYPE_CONTROL ){ |
| 1950 | Blob comment; |
| 1951 | int i; |
| 1952 | const char *zName; |
| 1953 | const char *zValue; |
| 1954 | const char *zTagUuid; |
| 1955 | int branchMove = 0; |
| 1956 | blob_zero(&comment); |
| 1957 | if( p->zComment ){ |
| 1958 | blob_appendf(&comment, " %s.", p->zComment); |
| 1959 | } |
| 1960 | /* Next loop expects tags to be sorted on UUID, so sort it. */ |
| 1961 | qsort(p->aTag, p->nTag, sizeof(p->aTag[0]), tag_compare); |
| 1962 | for(i=0; i<p->nTag; i++){ |
| 1963 | zTagUuid = p->aTag[i].zUuid; |
| 1964 | if( !zTagUuid ) continue; |
| 1965 | if( i==0 || fossil_strcmp(zTagUuid, p->aTag[i-1].zUuid)!=0 ){ |
| 1966 | blob_appendf(&comment, |
| 1967 | " Edit [%S]:", |
| 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; |
| 1979 | if( strcmp(zName, "*branch")==0 ){ |
| 1980 | blob_appendf(&comment, |
| 1981 | " Move to branch [/timeline?r=%h&nd&dp=%S&unhide | %h].", |
| 1982 | zValue, zTagUuid, zValue); |
| 1983 | branchMove = 1; |
| 1984 | continue; |
| 1985 | }else if( strcmp(zName, "*bgcolor")==0 ){ |
| 1986 | blob_appendf(&comment, |
| 1987 | " Change branch background color to \"%h\".", zValue); |
| @@ -2021,17 +2042,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 | ** |
| @@ -2045,7 +2072,7 @@ | |
| 2072 | Blob content; |
| 2073 | db_find_and_open_repository(0, 0); |
| 2074 | if( g.argc!=3 ) usage("RECORDID"); |
| 2075 | rid = name_to_rid(g.argv[2]); |
| 2076 | content_get(rid, &content); |
| 2077 | manifest_crosslink(rid, &content, MC_NONE); |
| 2078 | } |
| 2079 |
+1
-1
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -250,11 +250,11 @@ | ||
| 250 | 250 | blob_copy(©, pBase); |
| 251 | 251 | pUse = © |
| 252 | 252 | } |
| 253 | 253 | if( zFNameFormat==0 ){ |
| 254 | 254 | /* We are doing "fossil rebuild" */ |
| 255 | - manifest_crosslink(rid, pUse); | |
| 255 | + manifest_crosslink(rid, pUse, MC_NONE); | |
| 256 | 256 | }else{ |
| 257 | 257 | /* We are doing "fossil deconstruct" */ |
| 258 | 258 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 259 | 259 | char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength); |
| 260 | 260 | blob_write_to_file(pUse,zFile); |
| 261 | 261 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -250,11 +250,11 @@ | |
| 250 | blob_copy(©, pBase); |
| 251 | pUse = © |
| 252 | } |
| 253 | if( zFNameFormat==0 ){ |
| 254 | /* We are doing "fossil rebuild" */ |
| 255 | manifest_crosslink(rid, pUse); |
| 256 | }else{ |
| 257 | /* We are doing "fossil deconstruct" */ |
| 258 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 259 | char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength); |
| 260 | blob_write_to_file(pUse,zFile); |
| 261 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -250,11 +250,11 @@ | |
| 250 | blob_copy(©, pBase); |
| 251 | pUse = © |
| 252 | } |
| 253 | if( zFNameFormat==0 ){ |
| 254 | /* We are doing "fossil rebuild" */ |
| 255 | manifest_crosslink(rid, pUse, MC_NONE); |
| 256 | }else{ |
| 257 | /* We are doing "fossil deconstruct" */ |
| 258 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 259 | char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength); |
| 260 | blob_write_to_file(pUse,zFile); |
| 261 |
+1
-1
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -812,11 +812,11 @@ | ||
| 812 | 812 | if( zQ && fossil_strcmp(zQ,zVal)!=0 ){ |
| 813 | 813 | login_verify_csrf_secret(); |
| 814 | 814 | db_set(zVar, zQ, 0); |
| 815 | 815 | zVal = zQ; |
| 816 | 816 | } |
| 817 | - @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" | |
| 817 | + @ <input type="text" id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" | |
| 818 | 818 | if( disabled ){ |
| 819 | 819 | @ disabled="disabled" |
| 820 | 820 | } |
| 821 | 821 | @ /> <b>%s(zLabel)</b> |
| 822 | 822 | } |
| 823 | 823 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -812,11 +812,11 @@ | |
| 812 | if( zQ && fossil_strcmp(zQ,zVal)!=0 ){ |
| 813 | login_verify_csrf_secret(); |
| 814 | db_set(zVar, zQ, 0); |
| 815 | zVal = zQ; |
| 816 | } |
| 817 | @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" |
| 818 | if( disabled ){ |
| 819 | @ disabled="disabled" |
| 820 | } |
| 821 | @ /> <b>%s(zLabel)</b> |
| 822 | } |
| 823 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -812,11 +812,11 @@ | |
| 812 | if( zQ && fossil_strcmp(zQ,zVal)!=0 ){ |
| 813 | login_verify_csrf_secret(); |
| 814 | db_set(zVar, zQ, 0); |
| 815 | zVal = zQ; |
| 816 | } |
| 817 | @ <input type="text" id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" |
| 818 | if( disabled ){ |
| 819 | @ disabled="disabled" |
| 820 | } |
| 821 | @ /> <b>%s(zLabel)</b> |
| 822 | } |
| 823 |
+5
-1
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -3036,11 +3036,14 @@ | ||
| 3036 | 3036 | if( seenInterrupt ){ |
| 3037 | 3037 | if( in!=0 ) break; |
| 3038 | 3038 | seenInterrupt = 0; |
| 3039 | 3039 | } |
| 3040 | 3040 | lineno++; |
| 3041 | - if( nSql==0 && _all_whitespace(zLine) ) continue; | |
| 3041 | + if( nSql==0 && _all_whitespace(zLine) ){ | |
| 3042 | + if( p->echoOn ) printf("%s\n", zLine); | |
| 3043 | + continue; | |
| 3044 | + } | |
| 3042 | 3045 | if( zLine && zLine[0]=='.' && nSql==0 ){ |
| 3043 | 3046 | if( p->echoOn ) printf("%s\n", zLine); |
| 3044 | 3047 | rc = do_meta_command(zLine, p); |
| 3045 | 3048 | if( rc==2 ){ /* exit requested */ |
| 3046 | 3049 | break; |
| @@ -3098,10 +3101,11 @@ | ||
| 3098 | 3101 | } |
| 3099 | 3102 | errCnt++; |
| 3100 | 3103 | } |
| 3101 | 3104 | nSql = 0; |
| 3102 | 3105 | }else if( nSql && _all_whitespace(zSql) ){ |
| 3106 | + if( p->echoOn ) printf("%s\n", zSql); | |
| 3103 | 3107 | nSql = 0; |
| 3104 | 3108 | } |
| 3105 | 3109 | } |
| 3106 | 3110 | if( nSql ){ |
| 3107 | 3111 | if( !_all_whitespace(zSql) ){ |
| 3108 | 3112 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -3036,11 +3036,14 @@ | |
| 3036 | if( seenInterrupt ){ |
| 3037 | if( in!=0 ) break; |
| 3038 | seenInterrupt = 0; |
| 3039 | } |
| 3040 | lineno++; |
| 3041 | if( nSql==0 && _all_whitespace(zLine) ) continue; |
| 3042 | if( zLine && zLine[0]=='.' && nSql==0 ){ |
| 3043 | if( p->echoOn ) printf("%s\n", zLine); |
| 3044 | rc = do_meta_command(zLine, p); |
| 3045 | if( rc==2 ){ /* exit requested */ |
| 3046 | break; |
| @@ -3098,10 +3101,11 @@ | |
| 3098 | } |
| 3099 | errCnt++; |
| 3100 | } |
| 3101 | nSql = 0; |
| 3102 | }else if( nSql && _all_whitespace(zSql) ){ |
| 3103 | nSql = 0; |
| 3104 | } |
| 3105 | } |
| 3106 | if( nSql ){ |
| 3107 | if( !_all_whitespace(zSql) ){ |
| 3108 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -3036,11 +3036,14 @@ | |
| 3036 | if( seenInterrupt ){ |
| 3037 | if( in!=0 ) break; |
| 3038 | seenInterrupt = 0; |
| 3039 | } |
| 3040 | lineno++; |
| 3041 | if( nSql==0 && _all_whitespace(zLine) ){ |
| 3042 | if( p->echoOn ) printf("%s\n", zLine); |
| 3043 | continue; |
| 3044 | } |
| 3045 | if( zLine && zLine[0]=='.' && nSql==0 ){ |
| 3046 | if( p->echoOn ) printf("%s\n", zLine); |
| 3047 | rc = do_meta_command(zLine, p); |
| 3048 | if( rc==2 ){ /* exit requested */ |
| 3049 | break; |
| @@ -3098,10 +3101,11 @@ | |
| 3101 | } |
| 3102 | errCnt++; |
| 3103 | } |
| 3104 | nSql = 0; |
| 3105 | }else if( nSql && _all_whitespace(zSql) ){ |
| 3106 | if( p->echoOn ) printf("%s\n", zSql); |
| 3107 | nSql = 0; |
| 3108 | } |
| 3109 | } |
| 3110 | if( nSql ){ |
| 3111 | if( !_all_whitespace(zSql) ){ |
| 3112 |
+204
-111
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -135,11 +135,11 @@ | ||
| 135 | 135 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 136 | 136 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 137 | 137 | */ |
| 138 | 138 | #define SQLITE_VERSION "3.8.3" |
| 139 | 139 | #define SQLITE_VERSION_NUMBER 3008003 |
| 140 | -#define SQLITE_SOURCE_ID "2013-12-17 16:32:56 93121d3097a43997af3c0de65bd9bd7663845fa2" | |
| 140 | +#define SQLITE_SOURCE_ID "2013-12-23 11:33:32 25b8a1c9ba77df3b7c78cbce922cb593d661696d" | |
| 141 | 141 | |
| 142 | 142 | /* |
| 143 | 143 | ** CAPI3REF: Run-Time Library Version Numbers |
| 144 | 144 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 145 | 145 | ** |
| @@ -9098,11 +9098,11 @@ | ||
| 9098 | 9098 | #define OP_String 25 /* synopsis: r[P2]='P4' (len=P1) */ |
| 9099 | 9099 | #define OP_Null 26 /* synopsis: r[P2..P3]=NULL */ |
| 9100 | 9100 | #define OP_Blob 27 /* synopsis: r[P2]=P4 (len=P1) */ |
| 9101 | 9101 | #define OP_Variable 28 /* synopsis: r[P2]=parameter(P1,P4) */ |
| 9102 | 9102 | #define OP_Move 29 /* synopsis: r[P2@P3]=r[P1@P3] */ |
| 9103 | -#define OP_Copy 30 /* synopsis: r[P2@P3]=r[P1@P3] */ | |
| 9103 | +#define OP_Copy 30 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ | |
| 9104 | 9104 | #define OP_SCopy 31 /* synopsis: r[P2]=r[P1] */ |
| 9105 | 9105 | #define OP_ResultRow 32 /* synopsis: output=r[P1@P2] */ |
| 9106 | 9106 | #define OP_CollSeq 33 |
| 9107 | 9107 | #define OP_AddImm 34 /* synopsis: r[P1]=r[P1]+P2 */ |
| 9108 | 9108 | #define OP_MustBeInt 35 |
| @@ -9263,11 +9263,11 @@ | ||
| 9263 | 9263 | |
| 9264 | 9264 | /* |
| 9265 | 9265 | ** Prototypes for the VDBE interface. See comments on the implementation |
| 9266 | 9266 | ** for a description of what each of these routines does. |
| 9267 | 9267 | */ |
| 9268 | -SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3*); | |
| 9268 | +SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse*); | |
| 9269 | 9269 | SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); |
| 9270 | 9270 | SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); |
| 9271 | 9271 | SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); |
| 9272 | 9272 | SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); |
| 9273 | 9273 | SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); |
| @@ -11003,10 +11003,11 @@ | ||
| 11003 | 11003 | u8 useSortingIdx; /* In direct mode, reference the sorting index rather |
| 11004 | 11004 | ** than the source table */ |
| 11005 | 11005 | int sortingIdx; /* Cursor number of the sorting index */ |
| 11006 | 11006 | int sortingIdxPTab; /* Cursor number of pseudo-table */ |
| 11007 | 11007 | int nSortingColumn; /* Number of columns in the sorting index */ |
| 11008 | + int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */ | |
| 11008 | 11009 | ExprList *pGroupBy; /* The group by clause */ |
| 11009 | 11010 | struct AggInfo_col { /* For each column used in source tables */ |
| 11010 | 11011 | Table *pTab; /* Source table */ |
| 11011 | 11012 | int iTable; /* Cursor number of the source table */ |
| 11012 | 11013 | int iColumn; /* Column number within the source table */ |
| @@ -12452,12 +12453,11 @@ | ||
| 12452 | 12453 | SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...); |
| 12453 | 12454 | SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); |
| 12454 | 12455 | SQLITE_PRIVATE u8 sqlite3HexToInt(int h); |
| 12455 | 12456 | SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); |
| 12456 | 12457 | |
| 12457 | -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \ | |
| 12458 | - defined(SQLITE_DEBUG_OS_TRACE) | |
| 12458 | +#if defined(SQLITE_TEST) | |
| 12459 | 12459 | SQLITE_PRIVATE const char *sqlite3ErrName(int); |
| 12460 | 12460 | #endif |
| 12461 | 12461 | |
| 12462 | 12462 | SQLITE_PRIVATE const char *sqlite3ErrStr(int); |
| 12463 | 12463 | SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); |
| @@ -13776,10 +13776,13 @@ | ||
| 13776 | 13776 | Op *aOp; /* Space to hold the virtual machine's program */ |
| 13777 | 13777 | Mem *aMem; /* The memory locations */ |
| 13778 | 13778 | Mem **apArg; /* Arguments to currently executing user function */ |
| 13779 | 13779 | Mem *aColName; /* Column names to return */ |
| 13780 | 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 | |
| 13781 | 13784 | int nMem; /* Number of memory locations currently allocated */ |
| 13782 | 13785 | int nOp; /* Number of instructions in the program */ |
| 13783 | 13786 | int nOpAlloc; /* Number of slots allocated for aOp[] */ |
| 13784 | 13787 | int nLabel; /* Number of labels used */ |
| 13785 | 13788 | int *aLabel; /* Space to hold the labels */ |
| @@ -15443,11 +15446,25 @@ | ||
| 15443 | 15446 | ** really care if the VFS receives and understands the information since it |
| 15444 | 15447 | ** is only a hint and can be safely ignored. The sqlite3OsFileControlHint() |
| 15445 | 15448 | ** routine has no return value since the return value would be meaningless. |
| 15446 | 15449 | */ |
| 15447 | 15450 | SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ |
| 15448 | - DO_OS_MALLOC_TEST(id); | |
| 15451 | +#ifdef SQLITE_TEST | |
| 15452 | + if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){ | |
| 15453 | + /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite | |
| 15454 | + ** is using a regular VFS, it is called after the corresponding | |
| 15455 | + ** transaction has been committed. Injecting a fault at this point | |
| 15456 | + ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM | |
| 15457 | + ** but the transaction is committed anyway. | |
| 15458 | + ** | |
| 15459 | + ** The core must call OsFileControl() though, not OsFileControlHint(), | |
| 15460 | + ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably | |
| 15461 | + ** means the commit really has failed and an error should be returned | |
| 15462 | + ** to the user. */ | |
| 15463 | + DO_OS_MALLOC_TEST(id); | |
| 15464 | + } | |
| 15465 | +#endif | |
| 15449 | 15466 | return id->pMethods->xFileControl(id, op, pArg); |
| 15450 | 15467 | } |
| 15451 | 15468 | SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){ |
| 15452 | 15469 | (void)id->pMethods->xFileControl(id, op, pArg); |
| 15453 | 15470 | } |
| @@ -23098,11 +23115,11 @@ | ||
| 23098 | 23115 | /* 25 */ "String" OpHelp("r[P2]='P4' (len=P1)"), |
| 23099 | 23116 | /* 26 */ "Null" OpHelp("r[P2..P3]=NULL"), |
| 23100 | 23117 | /* 27 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), |
| 23101 | 23118 | /* 28 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), |
| 23102 | 23119 | /* 29 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), |
| 23103 | - /* 30 */ "Copy" OpHelp("r[P2@P3]=r[P1@P3]"), | |
| 23120 | + /* 30 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), | |
| 23104 | 23121 | /* 31 */ "SCopy" OpHelp("r[P2]=r[P1]"), |
| 23105 | 23122 | /* 32 */ "ResultRow" OpHelp("output=r[P1@P2]"), |
| 23106 | 23123 | /* 33 */ "CollSeq" OpHelp(""), |
| 23107 | 23124 | /* 34 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), |
| 23108 | 23125 | /* 35 */ "MustBeInt" OpHelp(""), |
| @@ -61223,11 +61240,12 @@ | ||
| 61223 | 61240 | */ |
| 61224 | 61241 | |
| 61225 | 61242 | /* |
| 61226 | 61243 | ** Create a new virtual database engine. |
| 61227 | 61244 | */ |
| 61228 | -SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){ | |
| 61245 | +SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){ | |
| 61246 | + sqlite3 *db = pParse->db; | |
| 61229 | 61247 | Vdbe *p; |
| 61230 | 61248 | p = sqlite3DbMallocZero(db, sizeof(Vdbe) ); |
| 61231 | 61249 | if( p==0 ) return 0; |
| 61232 | 61250 | p->db = db; |
| 61233 | 61251 | if( db->pVdbe ){ |
| @@ -61235,10 +61253,13 @@ | ||
| 61235 | 61253 | } |
| 61236 | 61254 | p->pNext = db->pVdbe; |
| 61237 | 61255 | p->pPrev = 0; |
| 61238 | 61256 | db->pVdbe = p; |
| 61239 | 61257 | p->magic = VDBE_MAGIC_INIT; |
| 61258 | +#if SQLITE_DEBUG | |
| 61259 | + p->pParse = pParse; | |
| 61260 | +#endif | |
| 61240 | 61261 | return p; |
| 61241 | 61262 | } |
| 61242 | 61263 | |
| 61243 | 61264 | /* |
| 61244 | 61265 | ** Remember the SQL string for a prepared statement. |
| @@ -61354,10 +61375,19 @@ | ||
| 61354 | 61375 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 61355 | 61376 | pOp->zComment = 0; |
| 61356 | 61377 | #endif |
| 61357 | 61378 | #ifdef SQLITE_DEBUG |
| 61358 | 61379 | if( p->db->flags & SQLITE_VdbeAddopTrace ){ |
| 61380 | + int jj, kk; | |
| 61381 | + Parse *pParse = p->pParse; | |
| 61382 | + for(jj=kk=0; jj<SQLITE_N_COLCACHE; jj++){ | |
| 61383 | + struct yColCache *x = pParse->aColCache + jj; | |
| 61384 | + if( x->iLevel>pParse->iCacheLevel || x->iReg==0 ) continue; | |
| 61385 | + printf(" r[%d]={%d:%d}", x->iReg, x->iTable, x->iColumn); | |
| 61386 | + kk++; | |
| 61387 | + } | |
| 61388 | + if( kk ) printf("\n"); | |
| 61359 | 61389 | sqlite3VdbePrintOp(0, i, &p->aOp[i]); |
| 61360 | 61390 | test_addop_breakpoint(); |
| 61361 | 61391 | } |
| 61362 | 61392 | #endif |
| 61363 | 61393 | #ifdef VDBE_PROFILE |
| @@ -62075,11 +62105,21 @@ | ||
| 62075 | 62105 | if( c=='4' ) return pOp->p4.i; |
| 62076 | 62106 | return pOp->p5; |
| 62077 | 62107 | } |
| 62078 | 62108 | |
| 62079 | 62109 | /* |
| 62080 | -** Compute a string for the "comment" field of a VDBE opcode listing | |
| 62110 | +** Compute a string for the "comment" field of a VDBE opcode listing. | |
| 62111 | +** | |
| 62112 | +** The Synopsis: field in comments in the vdbe.c source file gets converted | |
| 62113 | +** to an extra string that is appended to the sqlite3OpcodeName(). In the | |
| 62114 | +** absence of other comments, this synopsis becomes the comment on the opcode. | |
| 62115 | +** Some translation occurs: | |
| 62116 | +** | |
| 62117 | +** "PX" -> "r[X]" | |
| 62118 | +** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1 | |
| 62119 | +** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0 | |
| 62120 | +** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x | |
| 62081 | 62121 | */ |
| 62082 | 62122 | static int displayComment( |
| 62083 | 62123 | const Op *pOp, /* The opcode to be commented */ |
| 62084 | 62124 | const char *zP4, /* Previously obtained value for P4 */ |
| 62085 | 62125 | char *zTemp, /* Write result here */ |
| @@ -62109,11 +62149,17 @@ | ||
| 62109 | 62149 | sqlite3_snprintf(nTemp-jj, zTemp+jj, "%d", v1); |
| 62110 | 62150 | if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){ |
| 62111 | 62151 | ii += 3; |
| 62112 | 62152 | jj += sqlite3Strlen30(zTemp+jj); |
| 62113 | 62153 | v2 = translateP(zSynopsis[ii], pOp); |
| 62114 | - if( v2>1 ) sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1); | |
| 62154 | + if( strncmp(zSynopsis+ii+1,"+1",2)==0 ){ | |
| 62155 | + ii += 2; | |
| 62156 | + v2++; | |
| 62157 | + } | |
| 62158 | + if( v2>1 ){ | |
| 62159 | + sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1); | |
| 62160 | + } | |
| 62115 | 62161 | }else if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){ |
| 62116 | 62162 | ii += 4; |
| 62117 | 62163 | } |
| 62118 | 62164 | } |
| 62119 | 62165 | jj += sqlite3Strlen30(zTemp+jj); |
| @@ -62341,10 +62387,13 @@ | ||
| 62341 | 62387 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 62342 | 62388 | displayComment(pOp, zP4, zCom, sizeof(zCom)); |
| 62343 | 62389 | #else |
| 62344 | 62390 | zCom[0] = 0 |
| 62345 | 62391 | #endif |
| 62392 | + /* NB: The sqlite3OpcodeName() function is implemented by code created | |
| 62393 | + ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the | |
| 62394 | + ** information from the vdbe.c source text */ | |
| 62346 | 62395 | fprintf(pOut, zFormat1, pc, |
| 62347 | 62396 | sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5, |
| 62348 | 62397 | zCom |
| 62349 | 62398 | ); |
| 62350 | 62399 | fflush(pOut); |
| @@ -67401,11 +67450,11 @@ | ||
| 67401 | 67450 | }while( n-- ); |
| 67402 | 67451 | break; |
| 67403 | 67452 | } |
| 67404 | 67453 | |
| 67405 | 67454 | /* Opcode: Copy P1 P2 P3 * * |
| 67406 | -** Synopsis: r[P2@P3]=r[P1@P3] | |
| 67455 | +** Synopsis: r[P2@P3+1]=r[P1@P3+1] | |
| 67407 | 67456 | ** |
| 67408 | 67457 | ** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. |
| 67409 | 67458 | ** |
| 67410 | 67459 | ** This instruction makes a deep copy of the value. A duplicate |
| 67411 | 67460 | ** is made of any string or blob constant. See also OP_SCopy. |
| @@ -68744,11 +68793,11 @@ | ||
| 68744 | 68793 | /* This is the common case where the desired content fits on the original |
| 68745 | 68794 | ** page - where the content is not on an overflow page */ |
| 68746 | 68795 | VdbeMemRelease(pDest); |
| 68747 | 68796 | sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], aType[p2], pDest); |
| 68748 | 68797 | }else{ |
| 68749 | - /* This branch happens only when content is on overflow pages */ | |
| 68798 | + /* This branch happens only when content is on overflow pages */ | |
| 68750 | 68799 | t = aType[p2]; |
| 68751 | 68800 | if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 |
| 68752 | 68801 | && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) |
| 68753 | 68802 | || (len = sqlite3VdbeSerialTypeLen(t))==0 |
| 68754 | 68803 | ){ |
| @@ -72862,11 +72911,11 @@ | ||
| 72862 | 72911 | sqlite3BtreeLeaveAll(db); |
| 72863 | 72912 | goto blob_open_out; |
| 72864 | 72913 | } |
| 72865 | 72914 | } |
| 72866 | 72915 | |
| 72867 | - pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db); | |
| 72916 | + pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse); | |
| 72868 | 72917 | assert( pBlob->pStmt || db->mallocFailed ); |
| 72869 | 72918 | if( pBlob->pStmt ){ |
| 72870 | 72919 | Vdbe *v = (Vdbe *)pBlob->pStmt; |
| 72871 | 72920 | int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 72872 | 72921 | |
| @@ -78416,10 +78465,15 @@ | ||
| 78416 | 78465 | ** added to the column cache after this call are removed when the |
| 78417 | 78466 | ** corresponding pop occurs. |
| 78418 | 78467 | */ |
| 78419 | 78468 | SQLITE_PRIVATE void sqlite3ExprCachePush(Parse *pParse){ |
| 78420 | 78469 | pParse->iCacheLevel++; |
| 78470 | +#ifdef SQLITE_DEBUG | |
| 78471 | + if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ | |
| 78472 | + printf("PUSH to %d\n", pParse->iCacheLevel); | |
| 78473 | + } | |
| 78474 | +#endif | |
| 78421 | 78475 | } |
| 78422 | 78476 | |
| 78423 | 78477 | /* |
| 78424 | 78478 | ** Remove from the column cache any entries that were added since the |
| 78425 | 78479 | ** the previous N Push operations. In other words, restore the cache |
| @@ -78429,10 +78483,15 @@ | ||
| 78429 | 78483 | int i; |
| 78430 | 78484 | struct yColCache *p; |
| 78431 | 78485 | assert( N>0 ); |
| 78432 | 78486 | assert( pParse->iCacheLevel>=N ); |
| 78433 | 78487 | pParse->iCacheLevel -= N; |
| 78488 | +#ifdef SQLITE_DEBUG | |
| 78489 | + if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ | |
| 78490 | + printf("POP to %d\n", pParse->iCacheLevel); | |
| 78491 | + } | |
| 78492 | +#endif | |
| 78434 | 78493 | for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ |
| 78435 | 78494 | if( p->iReg && p->iLevel>pParse->iCacheLevel ){ |
| 78436 | 78495 | cacheEntryClear(pParse, p); |
| 78437 | 78496 | p->iReg = 0; |
| 78438 | 78497 | } |
| @@ -78523,10 +78582,15 @@ | ||
| 78523 | 78582 | */ |
| 78524 | 78583 | SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse *pParse){ |
| 78525 | 78584 | int i; |
| 78526 | 78585 | struct yColCache *p; |
| 78527 | 78586 | |
| 78587 | +#if SQLITE_DEBUG | |
| 78588 | + if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ | |
| 78589 | + printf("CLEAR\n"); | |
| 78590 | + } | |
| 78591 | +#endif | |
| 78528 | 78592 | for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ |
| 78529 | 78593 | if( p->iReg ){ |
| 78530 | 78594 | cacheEntryClear(pParse, p); |
| 78531 | 78595 | p->iReg = 0; |
| 78532 | 78596 | } |
| @@ -79659,11 +79723,21 @@ | ||
| 79659 | 79723 | if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){ |
| 79660 | 79724 | sqlite3ExprCodeAtInit(pParse, pExpr, target+i, 0); |
| 79661 | 79725 | }else{ |
| 79662 | 79726 | int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); |
| 79663 | 79727 | if( inReg!=target+i ){ |
| 79664 | - sqlite3VdbeAddOp2(pParse->pVdbe, copyOp, inReg, target+i); | |
| 79728 | + VdbeOp *pOp; | |
| 79729 | + Vdbe *v = pParse->pVdbe; | |
| 79730 | + if( copyOp==OP_Copy | |
| 79731 | + && (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy | |
| 79732 | + && pOp->p1+pOp->p3+1==inReg | |
| 79733 | + && pOp->p2+pOp->p3+1==target+i | |
| 79734 | + ){ | |
| 79735 | + pOp->p3++; | |
| 79736 | + }else{ | |
| 79737 | + sqlite3VdbeAddOp2(v, copyOp, inReg, target+i); | |
| 79738 | + } | |
| 79665 | 79739 | } |
| 79666 | 79740 | } |
| 79667 | 79741 | } |
| 79668 | 79742 | return n; |
| 79669 | 79743 | } |
| @@ -83083,14 +83157,10 @@ | ||
| 83083 | 83157 | { |
| 83084 | 83158 | int rc = SQLITE_OK; |
| 83085 | 83159 | if( pExpr ){ |
| 83086 | 83160 | if( pExpr->op!=TK_ID ){ |
| 83087 | 83161 | rc = sqlite3ResolveExprNames(pName, pExpr); |
| 83088 | - if( rc==SQLITE_OK && !sqlite3ExprIsConstant(pExpr) ){ | |
| 83089 | - sqlite3ErrorMsg(pName->pParse, "invalid name: \"%s\"", pExpr->u.zToken); | |
| 83090 | - return SQLITE_ERROR; | |
| 83091 | - } | |
| 83092 | 83162 | }else{ |
| 83093 | 83163 | pExpr->op = TK_STRING; |
| 83094 | 83164 | } |
| 83095 | 83165 | } |
| 83096 | 83166 | return rc; |
| @@ -89324,11 +89394,10 @@ | ||
| 89324 | 89394 | Vdbe *v = pParse->pVdbe; |
| 89325 | 89395 | int j; |
| 89326 | 89396 | Table *pTab = pIdx->pTable; |
| 89327 | 89397 | int regBase; |
| 89328 | 89398 | int nCol; |
| 89329 | - Index *pPk; | |
| 89330 | 89399 | |
| 89331 | 89400 | if( piPartIdxLabel ){ |
| 89332 | 89401 | if( pIdx->pPartIdxWhere ){ |
| 89333 | 89402 | *piPartIdxLabel = sqlite3VdbeMakeLabel(v); |
| 89334 | 89403 | pParse->iPartIdxTab = iDataCur; |
| @@ -89338,20 +89407,13 @@ | ||
| 89338 | 89407 | *piPartIdxLabel = 0; |
| 89339 | 89408 | } |
| 89340 | 89409 | } |
| 89341 | 89410 | nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn; |
| 89342 | 89411 | regBase = sqlite3GetTempRange(pParse, nCol); |
| 89343 | - pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); | |
| 89344 | 89412 | for(j=0; j<nCol; j++){ |
| 89345 | - i16 idx = pIdx->aiColumn[j]; | |
| 89346 | - if( pPk ) idx = sqlite3ColumnOfIndex(pPk, idx); | |
| 89347 | - if( idx<0 || idx==pTab->iPKey ){ | |
| 89348 | - sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regBase+j); | |
| 89349 | - }else{ | |
| 89350 | - sqlite3VdbeAddOp3(v, OP_Column, iDataCur, idx, regBase+j); | |
| 89351 | - sqlite3ColumnDefault(v, pTab, pIdx->aiColumn[j], -1); | |
| 89352 | - } | |
| 89413 | + sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j], | |
| 89414 | + regBase+j); | |
| 89353 | 89415 | } |
| 89354 | 89416 | if( regOut ){ |
| 89355 | 89417 | const char *zAff; |
| 89356 | 89418 | if( pTab->pSelect |
| 89357 | 89419 | || OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt) |
| @@ -93982,51 +94044,53 @@ | ||
| 93982 | 94044 | sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk, |
| 93983 | 94045 | regIdx, pIdx->nKeyCol); |
| 93984 | 94046 | |
| 93985 | 94047 | /* Generate code to handle collisions */ |
| 93986 | 94048 | regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField); |
| 93987 | - if( HasRowid(pTab) ){ | |
| 93988 | - sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); | |
| 93989 | - /* Conflict only if the rowid of the existing index entry | |
| 93990 | - ** is different from old-rowid */ | |
| 93991 | - if( isUpdate ){ | |
| 93992 | - sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData); | |
| 93993 | - } | |
| 93994 | - }else{ | |
| 93995 | - int x; | |
| 93996 | - /* Extract the PRIMARY KEY from the end of the index entry and | |
| 93997 | - ** store it in registers regR..regR+nPk-1 */ | |
| 93998 | - if( (isUpdate || onError==OE_Replace) && pIdx!=pPk ){ | |
| 93999 | - for(i=0; i<pPk->nKeyCol; i++){ | |
| 94000 | - x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]); | |
| 94001 | - sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); | |
| 94002 | - VdbeComment((v, "%s.%s", pTab->zName, | |
| 94003 | - pTab->aCol[pPk->aiColumn[i]].zName)); | |
| 94004 | - } | |
| 94005 | - } | |
| 94006 | - if( isUpdate ){ | |
| 94007 | - /* If currently processing the PRIMARY KEY of a WITHOUT ROWID | |
| 94008 | - ** table, only conflict if the new PRIMARY KEY values are actually | |
| 94009 | - ** different from the old. | |
| 94010 | - ** | |
| 94011 | - ** For a UNIQUE index, only conflict if the PRIMARY KEY values | |
| 94012 | - ** of the matched index row are different from the original PRIMARY | |
| 94013 | - ** KEY values of this row before the update. */ | |
| 94014 | - int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; | |
| 94015 | - int op = OP_Ne; | |
| 94016 | - int regCmp = (pIdx->autoIndex==2 ? regIdx : regR); | |
| 94017 | - | |
| 94018 | - for(i=0; i<pPk->nKeyCol; i++){ | |
| 94019 | - char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]); | |
| 94020 | - x = pPk->aiColumn[i]; | |
| 94021 | - if( i==(pPk->nKeyCol-1) ){ | |
| 94022 | - addrJump = addrUniqueOk; | |
| 94023 | - op = OP_Eq; | |
| 94024 | - } | |
| 94025 | - sqlite3VdbeAddOp4(v, op, | |
| 94026 | - regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ | |
| 94027 | - ); | |
| 94049 | + if( isUpdate || onError==OE_Replace ){ | |
| 94050 | + if( HasRowid(pTab) ){ | |
| 94051 | + sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); | |
| 94052 | + /* Conflict only if the rowid of the existing index entry | |
| 94053 | + ** is different from old-rowid */ | |
| 94054 | + if( isUpdate ){ | |
| 94055 | + sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData); | |
| 94056 | + } | |
| 94057 | + }else{ | |
| 94058 | + int x; | |
| 94059 | + /* Extract the PRIMARY KEY from the end of the index entry and | |
| 94060 | + ** store it in registers regR..regR+nPk-1 */ | |
| 94061 | + if( pIdx!=pPk ){ | |
| 94062 | + for(i=0; i<pPk->nKeyCol; i++){ | |
| 94063 | + x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]); | |
| 94064 | + sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); | |
| 94065 | + VdbeComment((v, "%s.%s", pTab->zName, | |
| 94066 | + pTab->aCol[pPk->aiColumn[i]].zName)); | |
| 94067 | + } | |
| 94068 | + } | |
| 94069 | + if( isUpdate ){ | |
| 94070 | + /* If currently processing the PRIMARY KEY of a WITHOUT ROWID | |
| 94071 | + ** table, only conflict if the new PRIMARY KEY values are actually | |
| 94072 | + ** different from the old. | |
| 94073 | + ** | |
| 94074 | + ** For a UNIQUE index, only conflict if the PRIMARY KEY values | |
| 94075 | + ** of the matched index row are different from the original PRIMARY | |
| 94076 | + ** KEY values of this row before the update. */ | |
| 94077 | + int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; | |
| 94078 | + int op = OP_Ne; | |
| 94079 | + int regCmp = (pIdx->autoIndex==2 ? regIdx : regR); | |
| 94080 | + | |
| 94081 | + for(i=0; i<pPk->nKeyCol; i++){ | |
| 94082 | + char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]); | |
| 94083 | + x = pPk->aiColumn[i]; | |
| 94084 | + if( i==(pPk->nKeyCol-1) ){ | |
| 94085 | + addrJump = addrUniqueOk; | |
| 94086 | + op = OP_Eq; | |
| 94087 | + } | |
| 94088 | + sqlite3VdbeAddOp4(v, op, | |
| 94089 | + regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ | |
| 94090 | + ); | |
| 94091 | + } | |
| 94028 | 94092 | } |
| 94029 | 94093 | } |
| 94030 | 94094 | } |
| 94031 | 94095 | |
| 94032 | 94096 | /* Generate code that executes if the new index entry is not unique */ |
| @@ -99722,11 +99786,10 @@ | ||
| 99722 | 99786 | } |
| 99723 | 99787 | }else if( eDest!=SRT_Exists ){ |
| 99724 | 99788 | /* If the destination is an EXISTS(...) expression, the actual |
| 99725 | 99789 | ** values returned by the SELECT are not required. |
| 99726 | 99790 | */ |
| 99727 | - sqlite3ExprCacheClear(pParse); | |
| 99728 | 99791 | sqlite3ExprCodeExprList(pParse, pEList, regResult, |
| 99729 | 99792 | (eDest==SRT_Output)?SQLITE_ECEL_DUP:0); |
| 99730 | 99793 | } |
| 99731 | 99794 | nColumn = nResultCol; |
| 99732 | 99795 | |
| @@ -100689,11 +100752,11 @@ | ||
| 100689 | 100752 | ** If an error occurs, return NULL and leave a message in pParse. |
| 100690 | 100753 | */ |
| 100691 | 100754 | SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){ |
| 100692 | 100755 | Vdbe *v = pParse->pVdbe; |
| 100693 | 100756 | if( v==0 ){ |
| 100694 | - v = pParse->pVdbe = sqlite3VdbeCreate(pParse->db); | |
| 100757 | + v = pParse->pVdbe = sqlite3VdbeCreate(pParse); | |
| 100695 | 100758 | #ifndef SQLITE_OMIT_TRACE |
| 100696 | 100759 | if( v ){ |
| 100697 | 100760 | sqlite3VdbeAddOp0(v, OP_Trace); |
| 100698 | 100761 | } |
| 100699 | 100762 | #endif |
| @@ -102947,18 +103010,27 @@ | ||
| 102947 | 103010 | */ |
| 102948 | 103011 | static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ |
| 102949 | 103012 | Vdbe *v = pParse->pVdbe; |
| 102950 | 103013 | int i; |
| 102951 | 103014 | struct AggInfo_func *pFunc; |
| 102952 | - if( pAggInfo->nFunc+pAggInfo->nColumn==0 ){ | |
| 102953 | - return; | |
| 102954 | - } | |
| 103015 | + int nReg = pAggInfo->nFunc + pAggInfo->nColumn; | |
| 103016 | + if( nReg==0 ) return; | |
| 103017 | +#ifdef SQLITE_DEBUG | |
| 103018 | + /* Verify that all AggInfo registers are within the range specified by | |
| 103019 | + ** AggInfo.mnReg..AggInfo.mxReg */ | |
| 103020 | + assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 ); | |
| 102955 | 103021 | for(i=0; i<pAggInfo->nColumn; i++){ |
| 102956 | - sqlite3VdbeAddOp2(v, OP_Null, 0, pAggInfo->aCol[i].iMem); | |
| 103022 | + assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg | |
| 103023 | + && pAggInfo->aCol[i].iMem<=pAggInfo->mxReg ); | |
| 102957 | 103024 | } |
| 103025 | + for(i=0; i<pAggInfo->nFunc; i++){ | |
| 103026 | + assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg | |
| 103027 | + && pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg ); | |
| 103028 | + } | |
| 103029 | +#endif | |
| 103030 | + sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg); | |
| 102958 | 103031 | for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){ |
| 102959 | - sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem); | |
| 102960 | 103032 | if( pFunc->iDistinct>=0 ){ |
| 102961 | 103033 | Expr *pE = pFunc->pExpr; |
| 102962 | 103034 | assert( !ExprHasProperty(pE, EP_xIsSelect) ); |
| 102963 | 103035 | if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ |
| 102964 | 103036 | sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " |
| @@ -103000,11 +103072,10 @@ | ||
| 103000 | 103072 | int addrHitTest = 0; |
| 103001 | 103073 | struct AggInfo_func *pF; |
| 103002 | 103074 | struct AggInfo_col *pC; |
| 103003 | 103075 | |
| 103004 | 103076 | pAggInfo->directMode = 1; |
| 103005 | - sqlite3ExprCacheClear(pParse); | |
| 103006 | 103077 | for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){ |
| 103007 | 103078 | int nArg; |
| 103008 | 103079 | int addrNext = 0; |
| 103009 | 103080 | int regAgg; |
| 103010 | 103081 | ExprList *pList = pF->pExpr->x.pList; |
| @@ -103533,10 +103604,11 @@ | ||
| 103533 | 103604 | */ |
| 103534 | 103605 | memset(&sNC, 0, sizeof(sNC)); |
| 103535 | 103606 | sNC.pParse = pParse; |
| 103536 | 103607 | sNC.pSrcList = pTabList; |
| 103537 | 103608 | sNC.pAggInfo = &sAggInfo; |
| 103609 | + sAggInfo.mnReg = pParse->nMem+1; | |
| 103538 | 103610 | sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0; |
| 103539 | 103611 | sAggInfo.pGroupBy = pGroupBy; |
| 103540 | 103612 | sqlite3ExprAnalyzeAggList(&sNC, pEList); |
| 103541 | 103613 | sqlite3ExprAnalyzeAggList(&sNC, pOrderBy); |
| 103542 | 103614 | if( pHaving ){ |
| @@ -103547,10 +103619,11 @@ | ||
| 103547 | 103619 | assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) ); |
| 103548 | 103620 | sNC.ncFlags |= NC_InAggFunc; |
| 103549 | 103621 | sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList); |
| 103550 | 103622 | sNC.ncFlags &= ~NC_InAggFunc; |
| 103551 | 103623 | } |
| 103624 | + sAggInfo.mxReg = pParse->nMem; | |
| 103552 | 103625 | if( db->mallocFailed ) goto select_end; |
| 103553 | 103626 | |
| 103554 | 103627 | /* Processing for aggregates with GROUP BY is very different and |
| 103555 | 103628 | ** much more complex than aggregates without a GROUP BY. |
| 103556 | 103629 | */ |
| @@ -105451,11 +105524,11 @@ | ||
| 105451 | 105524 | pCol->affinity, &pValue); |
| 105452 | 105525 | if( pValue ){ |
| 105453 | 105526 | sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM); |
| 105454 | 105527 | } |
| 105455 | 105528 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 105456 | - if( iReg>=0 && pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ | |
| 105529 | + if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ | |
| 105457 | 105530 | sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); |
| 105458 | 105531 | } |
| 105459 | 105532 | #endif |
| 105460 | 105533 | } |
| 105461 | 105534 | } |
| @@ -105875,14 +105948,14 @@ | ||
| 105875 | 105948 | ** be used eliminates some redundant opcodes. |
| 105876 | 105949 | */ |
| 105877 | 105950 | newmask = sqlite3TriggerColmask( |
| 105878 | 105951 | pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError |
| 105879 | 105952 | ); |
| 105880 | - sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1); | |
| 105953 | + /*sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);*/ | |
| 105881 | 105954 | for(i=0; i<pTab->nCol; i++){ |
| 105882 | 105955 | if( i==pTab->iPKey ){ |
| 105883 | - /*sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);*/ | |
| 105956 | + sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); | |
| 105884 | 105957 | }else{ |
| 105885 | 105958 | j = aXRef[i]; |
| 105886 | 105959 | if( j>=0 ){ |
| 105887 | 105960 | sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); |
| 105888 | 105961 | }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<<i)) ){ |
| @@ -105892,10 +105965,12 @@ | ||
| 105892 | 105965 | ** a new.* reference in a trigger program. |
| 105893 | 105966 | */ |
| 105894 | 105967 | testcase( i==31 ); |
| 105895 | 105968 | testcase( i==32 ); |
| 105896 | 105969 | sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i); |
| 105970 | + }else{ | |
| 105971 | + sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); | |
| 105897 | 105972 | } |
| 105898 | 105973 | } |
| 105899 | 105974 | } |
| 105900 | 105975 | |
| 105901 | 105976 | /* Fire any BEFORE UPDATE triggers. This happens before constraints are |
| @@ -112004,10 +112079,11 @@ | ||
| 112004 | 112079 | */ |
| 112005 | 112080 | if( pTerm==0 |
| 112006 | 112081 | && saved_nEq==saved_nSkip |
| 112007 | 112082 | && saved_nEq+1<pProbe->nKeyCol |
| 112008 | 112083 | && pProbe->aiRowEst[saved_nEq+1]>=18 /* TUNING: Minimum for skip-scan */ |
| 112084 | + && (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK | |
| 112009 | 112085 | ){ |
| 112010 | 112086 | LogEst nIter; |
| 112011 | 112087 | pNew->u.btree.nEq++; |
| 112012 | 112088 | pNew->u.btree.nSkip++; |
| 112013 | 112089 | pNew->aLTerm[pNew->nLTerm++] = 0; |
| @@ -119722,12 +119798,11 @@ | ||
| 119722 | 119798 | |
| 119723 | 119799 | /* |
| 119724 | 119800 | ** Return a static string containing the name corresponding to the error code |
| 119725 | 119801 | ** specified in the argument. |
| 119726 | 119802 | */ |
| 119727 | -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \ | |
| 119728 | - defined(SQLITE_DEBUG_OS_TRACE) | |
| 119803 | +#if defined(SQLITE_TEST) | |
| 119729 | 119804 | SQLITE_PRIVATE const char *sqlite3ErrName(int rc){ |
| 119730 | 119805 | const char *zName = 0; |
| 119731 | 119806 | int i, origRc = rc; |
| 119732 | 119807 | for(i=0; i<2 && zName==0; i++, rc &= 0xff){ |
| 119733 | 119808 | switch( rc ){ |
| @@ -121300,10 +121375,12 @@ | ||
| 121300 | 121375 | #ifdef SQLITE_DEFAULT_LOCKING_MODE |
| 121301 | 121376 | db->dfltLockMode = SQLITE_DEFAULT_LOCKING_MODE; |
| 121302 | 121377 | sqlite3PagerLockingMode(sqlite3BtreePager(db->aDb[0].pBt), |
| 121303 | 121378 | SQLITE_DEFAULT_LOCKING_MODE); |
| 121304 | 121379 | #endif |
| 121380 | + | |
| 121381 | + if( rc ) sqlite3Error(db, rc, 0); | |
| 121305 | 121382 | |
| 121306 | 121383 | /* Enable the lookaside-malloc subsystem */ |
| 121307 | 121384 | setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside, |
| 121308 | 121385 | sqlite3GlobalConfig.nLookaside); |
| 121309 | 121386 | |
| @@ -131632,61 +131709,74 @@ | ||
| 131632 | 131709 | } |
| 131633 | 131710 | |
| 131634 | 131711 | /* Step 2 */ |
| 131635 | 131712 | switch( z[1] ){ |
| 131636 | 131713 | case 'a': |
| 131637 | - stem(&z, "lanoita", "ate", m_gt_0) || | |
| 131638 | - stem(&z, "lanoit", "tion", m_gt_0); | |
| 131714 | + if( !stem(&z, "lanoita", "ate", m_gt_0) ){ | |
| 131715 | + stem(&z, "lanoit", "tion", m_gt_0); | |
| 131716 | + } | |
| 131639 | 131717 | break; |
| 131640 | 131718 | case 'c': |
| 131641 | - stem(&z, "icne", "ence", m_gt_0) || | |
| 131642 | - stem(&z, "icna", "ance", m_gt_0); | |
| 131719 | + if( !stem(&z, "icne", "ence", m_gt_0) ){ | |
| 131720 | + stem(&z, "icna", "ance", m_gt_0); | |
| 131721 | + } | |
| 131643 | 131722 | break; |
| 131644 | 131723 | case 'e': |
| 131645 | 131724 | stem(&z, "rezi", "ize", m_gt_0); |
| 131646 | 131725 | break; |
| 131647 | 131726 | case 'g': |
| 131648 | 131727 | stem(&z, "igol", "log", m_gt_0); |
| 131649 | 131728 | break; |
| 131650 | 131729 | case 'l': |
| 131651 | - stem(&z, "ilb", "ble", m_gt_0) || | |
| 131652 | - stem(&z, "illa", "al", m_gt_0) || | |
| 131653 | - stem(&z, "iltne", "ent", m_gt_0) || | |
| 131654 | - stem(&z, "ile", "e", m_gt_0) || | |
| 131655 | - stem(&z, "ilsuo", "ous", m_gt_0); | |
| 131730 | + if( !stem(&z, "ilb", "ble", m_gt_0) | |
| 131731 | + && !stem(&z, "illa", "al", m_gt_0) | |
| 131732 | + && !stem(&z, "iltne", "ent", m_gt_0) | |
| 131733 | + && !stem(&z, "ile", "e", m_gt_0) | |
| 131734 | + ){ | |
| 131735 | + stem(&z, "ilsuo", "ous", m_gt_0); | |
| 131736 | + } | |
| 131656 | 131737 | break; |
| 131657 | 131738 | case 'o': |
| 131658 | - stem(&z, "noitazi", "ize", m_gt_0) || | |
| 131659 | - stem(&z, "noita", "ate", m_gt_0) || | |
| 131660 | - stem(&z, "rota", "ate", m_gt_0); | |
| 131739 | + if( !stem(&z, "noitazi", "ize", m_gt_0) | |
| 131740 | + && !stem(&z, "noita", "ate", m_gt_0) | |
| 131741 | + ){ | |
| 131742 | + stem(&z, "rota", "ate", m_gt_0); | |
| 131743 | + } | |
| 131661 | 131744 | break; |
| 131662 | 131745 | case 's': |
| 131663 | - stem(&z, "msila", "al", m_gt_0) || | |
| 131664 | - stem(&z, "ssenevi", "ive", m_gt_0) || | |
| 131665 | - stem(&z, "ssenluf", "ful", m_gt_0) || | |
| 131666 | - stem(&z, "ssensuo", "ous", m_gt_0); | |
| 131746 | + if( !stem(&z, "msila", "al", m_gt_0) | |
| 131747 | + && !stem(&z, "ssenevi", "ive", m_gt_0) | |
| 131748 | + && !stem(&z, "ssenluf", "ful", m_gt_0) | |
| 131749 | + ){ | |
| 131750 | + stem(&z, "ssensuo", "ous", m_gt_0); | |
| 131751 | + } | |
| 131667 | 131752 | break; |
| 131668 | 131753 | case 't': |
| 131669 | - stem(&z, "itila", "al", m_gt_0) || | |
| 131670 | - stem(&z, "itivi", "ive", m_gt_0) || | |
| 131671 | - stem(&z, "itilib", "ble", m_gt_0); | |
| 131754 | + if( !stem(&z, "itila", "al", m_gt_0) | |
| 131755 | + && !stem(&z, "itivi", "ive", m_gt_0) | |
| 131756 | + ){ | |
| 131757 | + stem(&z, "itilib", "ble", m_gt_0); | |
| 131758 | + } | |
| 131672 | 131759 | break; |
| 131673 | 131760 | } |
| 131674 | 131761 | |
| 131675 | 131762 | /* Step 3 */ |
| 131676 | 131763 | switch( z[0] ){ |
| 131677 | 131764 | case 'e': |
| 131678 | - stem(&z, "etaci", "ic", m_gt_0) || | |
| 131679 | - stem(&z, "evita", "", m_gt_0) || | |
| 131680 | - stem(&z, "ezila", "al", m_gt_0); | |
| 131765 | + if( !stem(&z, "etaci", "ic", m_gt_0) | |
| 131766 | + && !stem(&z, "evita", "", m_gt_0) | |
| 131767 | + ){ | |
| 131768 | + stem(&z, "ezila", "al", m_gt_0); | |
| 131769 | + } | |
| 131681 | 131770 | break; |
| 131682 | 131771 | case 'i': |
| 131683 | 131772 | stem(&z, "itici", "ic", m_gt_0); |
| 131684 | 131773 | break; |
| 131685 | 131774 | case 'l': |
| 131686 | - stem(&z, "laci", "ic", m_gt_0) || | |
| 131687 | - stem(&z, "luf", "", m_gt_0); | |
| 131775 | + if( !stem(&z, "laci", "ic", m_gt_0) ){ | |
| 131776 | + stem(&z, "luf", "", m_gt_0); | |
| 131777 | + } | |
| 131688 | 131778 | break; |
| 131689 | 131779 | case 's': |
| 131690 | 131780 | stem(&z, "ssen", "", m_gt_0); |
| 131691 | 131781 | break; |
| 131692 | 131782 | } |
| @@ -131723,13 +131813,15 @@ | ||
| 131723 | 131813 | if( z[2]=='a' ){ |
| 131724 | 131814 | if( m_gt_1(z+3) ){ |
| 131725 | 131815 | z += 3; |
| 131726 | 131816 | } |
| 131727 | 131817 | }else if( z[2]=='e' ){ |
| 131728 | - stem(&z, "tneme", "", m_gt_1) || | |
| 131729 | - stem(&z, "tnem", "", m_gt_1) || | |
| 131730 | - stem(&z, "tne", "", m_gt_1); | |
| 131818 | + if( !stem(&z, "tneme", "", m_gt_1) | |
| 131819 | + && !stem(&z, "tnem", "", m_gt_1) | |
| 131820 | + ){ | |
| 131821 | + stem(&z, "tne", "", m_gt_1); | |
| 131822 | + } | |
| 131731 | 131823 | } |
| 131732 | 131824 | } |
| 131733 | 131825 | break; |
| 131734 | 131826 | case 'o': |
| 131735 | 131827 | if( z[0]=='u' ){ |
| @@ -131744,12 +131836,13 @@ | ||
| 131744 | 131836 | if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){ |
| 131745 | 131837 | z += 3; |
| 131746 | 131838 | } |
| 131747 | 131839 | break; |
| 131748 | 131840 | case 't': |
| 131749 | - stem(&z, "eta", "", m_gt_1) || | |
| 131750 | - stem(&z, "iti", "", m_gt_1); | |
| 131841 | + if( !stem(&z, "eta", "", m_gt_1) ){ | |
| 131842 | + stem(&z, "iti", "", m_gt_1); | |
| 131843 | + } | |
| 131751 | 131844 | break; |
| 131752 | 131845 | case 'u': |
| 131753 | 131846 | if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){ |
| 131754 | 131847 | z += 3; |
| 131755 | 131848 | } |
| 131756 | 131849 |
| --- 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-17 16:32:56 93121d3097a43997af3c0de65bd9bd7663845fa2" |
| 141 | |
| 142 | /* |
| 143 | ** CAPI3REF: Run-Time Library Version Numbers |
| 144 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 145 | ** |
| @@ -9098,11 +9098,11 @@ | |
| 9098 | #define OP_String 25 /* synopsis: r[P2]='P4' (len=P1) */ |
| 9099 | #define OP_Null 26 /* synopsis: r[P2..P3]=NULL */ |
| 9100 | #define OP_Blob 27 /* synopsis: r[P2]=P4 (len=P1) */ |
| 9101 | #define OP_Variable 28 /* synopsis: r[P2]=parameter(P1,P4) */ |
| 9102 | #define OP_Move 29 /* synopsis: r[P2@P3]=r[P1@P3] */ |
| 9103 | #define OP_Copy 30 /* synopsis: r[P2@P3]=r[P1@P3] */ |
| 9104 | #define OP_SCopy 31 /* synopsis: r[P2]=r[P1] */ |
| 9105 | #define OP_ResultRow 32 /* synopsis: output=r[P1@P2] */ |
| 9106 | #define OP_CollSeq 33 |
| 9107 | #define OP_AddImm 34 /* synopsis: r[P1]=r[P1]+P2 */ |
| 9108 | #define OP_MustBeInt 35 |
| @@ -9263,11 +9263,11 @@ | |
| 9263 | |
| 9264 | /* |
| 9265 | ** Prototypes for the VDBE interface. See comments on the implementation |
| 9266 | ** for a description of what each of these routines does. |
| 9267 | */ |
| 9268 | SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3*); |
| 9269 | SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); |
| 9270 | SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); |
| 9271 | SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); |
| 9272 | SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); |
| 9273 | SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); |
| @@ -11003,10 +11003,11 @@ | |
| 11003 | u8 useSortingIdx; /* In direct mode, reference the sorting index rather |
| 11004 | ** than the source table */ |
| 11005 | int sortingIdx; /* Cursor number of the sorting index */ |
| 11006 | int sortingIdxPTab; /* Cursor number of pseudo-table */ |
| 11007 | int nSortingColumn; /* Number of columns in the sorting index */ |
| 11008 | ExprList *pGroupBy; /* The group by clause */ |
| 11009 | struct AggInfo_col { /* For each column used in source tables */ |
| 11010 | Table *pTab; /* Source table */ |
| 11011 | int iTable; /* Cursor number of the source table */ |
| 11012 | int iColumn; /* Column number within the source table */ |
| @@ -12452,12 +12453,11 @@ | |
| 12452 | SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...); |
| 12453 | SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); |
| 12454 | SQLITE_PRIVATE u8 sqlite3HexToInt(int h); |
| 12455 | SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); |
| 12456 | |
| 12457 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \ |
| 12458 | defined(SQLITE_DEBUG_OS_TRACE) |
| 12459 | SQLITE_PRIVATE const char *sqlite3ErrName(int); |
| 12460 | #endif |
| 12461 | |
| 12462 | SQLITE_PRIVATE const char *sqlite3ErrStr(int); |
| 12463 | SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); |
| @@ -13776,10 +13776,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 | int nMem; /* Number of memory locations currently allocated */ |
| 13782 | int nOp; /* Number of instructions in the program */ |
| 13783 | int nOpAlloc; /* Number of slots allocated for aOp[] */ |
| 13784 | int nLabel; /* Number of labels used */ |
| 13785 | int *aLabel; /* Space to hold the labels */ |
| @@ -15443,11 +15446,25 @@ | |
| 15443 | ** really care if the VFS receives and understands the information since it |
| 15444 | ** is only a hint and can be safely ignored. The sqlite3OsFileControlHint() |
| 15445 | ** routine has no return value since the return value would be meaningless. |
| 15446 | */ |
| 15447 | SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ |
| 15448 | DO_OS_MALLOC_TEST(id); |
| 15449 | return id->pMethods->xFileControl(id, op, pArg); |
| 15450 | } |
| 15451 | SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){ |
| 15452 | (void)id->pMethods->xFileControl(id, op, pArg); |
| 15453 | } |
| @@ -23098,11 +23115,11 @@ | |
| 23098 | /* 25 */ "String" OpHelp("r[P2]='P4' (len=P1)"), |
| 23099 | /* 26 */ "Null" OpHelp("r[P2..P3]=NULL"), |
| 23100 | /* 27 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), |
| 23101 | /* 28 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), |
| 23102 | /* 29 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), |
| 23103 | /* 30 */ "Copy" OpHelp("r[P2@P3]=r[P1@P3]"), |
| 23104 | /* 31 */ "SCopy" OpHelp("r[P2]=r[P1]"), |
| 23105 | /* 32 */ "ResultRow" OpHelp("output=r[P1@P2]"), |
| 23106 | /* 33 */ "CollSeq" OpHelp(""), |
| 23107 | /* 34 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), |
| 23108 | /* 35 */ "MustBeInt" OpHelp(""), |
| @@ -61223,11 +61240,12 @@ | |
| 61223 | */ |
| 61224 | |
| 61225 | /* |
| 61226 | ** Create a new virtual database engine. |
| 61227 | */ |
| 61228 | SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){ |
| 61229 | Vdbe *p; |
| 61230 | p = sqlite3DbMallocZero(db, sizeof(Vdbe) ); |
| 61231 | if( p==0 ) return 0; |
| 61232 | p->db = db; |
| 61233 | if( db->pVdbe ){ |
| @@ -61235,10 +61253,13 @@ | |
| 61235 | } |
| 61236 | p->pNext = db->pVdbe; |
| 61237 | p->pPrev = 0; |
| 61238 | db->pVdbe = p; |
| 61239 | p->magic = VDBE_MAGIC_INIT; |
| 61240 | return p; |
| 61241 | } |
| 61242 | |
| 61243 | /* |
| 61244 | ** Remember the SQL string for a prepared statement. |
| @@ -61354,10 +61375,19 @@ | |
| 61354 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 61355 | pOp->zComment = 0; |
| 61356 | #endif |
| 61357 | #ifdef SQLITE_DEBUG |
| 61358 | if( p->db->flags & SQLITE_VdbeAddopTrace ){ |
| 61359 | sqlite3VdbePrintOp(0, i, &p->aOp[i]); |
| 61360 | test_addop_breakpoint(); |
| 61361 | } |
| 61362 | #endif |
| 61363 | #ifdef VDBE_PROFILE |
| @@ -62075,11 +62105,21 @@ | |
| 62075 | if( c=='4' ) return pOp->p4.i; |
| 62076 | return pOp->p5; |
| 62077 | } |
| 62078 | |
| 62079 | /* |
| 62080 | ** Compute a string for the "comment" field of a VDBE opcode listing |
| 62081 | */ |
| 62082 | static int displayComment( |
| 62083 | const Op *pOp, /* The opcode to be commented */ |
| 62084 | const char *zP4, /* Previously obtained value for P4 */ |
| 62085 | char *zTemp, /* Write result here */ |
| @@ -62109,11 +62149,17 @@ | |
| 62109 | sqlite3_snprintf(nTemp-jj, zTemp+jj, "%d", v1); |
| 62110 | if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){ |
| 62111 | ii += 3; |
| 62112 | jj += sqlite3Strlen30(zTemp+jj); |
| 62113 | v2 = translateP(zSynopsis[ii], pOp); |
| 62114 | if( v2>1 ) sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1); |
| 62115 | }else if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){ |
| 62116 | ii += 4; |
| 62117 | } |
| 62118 | } |
| 62119 | jj += sqlite3Strlen30(zTemp+jj); |
| @@ -62341,10 +62387,13 @@ | |
| 62341 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 62342 | displayComment(pOp, zP4, zCom, sizeof(zCom)); |
| 62343 | #else |
| 62344 | zCom[0] = 0 |
| 62345 | #endif |
| 62346 | fprintf(pOut, zFormat1, pc, |
| 62347 | sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5, |
| 62348 | zCom |
| 62349 | ); |
| 62350 | fflush(pOut); |
| @@ -67401,11 +67450,11 @@ | |
| 67401 | }while( n-- ); |
| 67402 | break; |
| 67403 | } |
| 67404 | |
| 67405 | /* Opcode: Copy P1 P2 P3 * * |
| 67406 | ** Synopsis: r[P2@P3]=r[P1@P3] |
| 67407 | ** |
| 67408 | ** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. |
| 67409 | ** |
| 67410 | ** This instruction makes a deep copy of the value. A duplicate |
| 67411 | ** is made of any string or blob constant. See also OP_SCopy. |
| @@ -68744,11 +68793,11 @@ | |
| 68744 | /* This is the common case where the desired content fits on the original |
| 68745 | ** page - where the content is not on an overflow page */ |
| 68746 | VdbeMemRelease(pDest); |
| 68747 | sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], aType[p2], pDest); |
| 68748 | }else{ |
| 68749 | /* This branch happens only when content is on overflow pages */ |
| 68750 | t = aType[p2]; |
| 68751 | if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 |
| 68752 | && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) |
| 68753 | || (len = sqlite3VdbeSerialTypeLen(t))==0 |
| 68754 | ){ |
| @@ -72862,11 +72911,11 @@ | |
| 72862 | sqlite3BtreeLeaveAll(db); |
| 72863 | goto blob_open_out; |
| 72864 | } |
| 72865 | } |
| 72866 | |
| 72867 | pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db); |
| 72868 | assert( pBlob->pStmt || db->mallocFailed ); |
| 72869 | if( pBlob->pStmt ){ |
| 72870 | Vdbe *v = (Vdbe *)pBlob->pStmt; |
| 72871 | int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 72872 | |
| @@ -78416,10 +78465,15 @@ | |
| 78416 | ** added to the column cache after this call are removed when the |
| 78417 | ** corresponding pop occurs. |
| 78418 | */ |
| 78419 | SQLITE_PRIVATE void sqlite3ExprCachePush(Parse *pParse){ |
| 78420 | pParse->iCacheLevel++; |
| 78421 | } |
| 78422 | |
| 78423 | /* |
| 78424 | ** Remove from the column cache any entries that were added since the |
| 78425 | ** the previous N Push operations. In other words, restore the cache |
| @@ -78429,10 +78483,15 @@ | |
| 78429 | int i; |
| 78430 | struct yColCache *p; |
| 78431 | assert( N>0 ); |
| 78432 | assert( pParse->iCacheLevel>=N ); |
| 78433 | pParse->iCacheLevel -= N; |
| 78434 | for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ |
| 78435 | if( p->iReg && p->iLevel>pParse->iCacheLevel ){ |
| 78436 | cacheEntryClear(pParse, p); |
| 78437 | p->iReg = 0; |
| 78438 | } |
| @@ -78523,10 +78582,15 @@ | |
| 78523 | */ |
| 78524 | SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse *pParse){ |
| 78525 | int i; |
| 78526 | struct yColCache *p; |
| 78527 | |
| 78528 | for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ |
| 78529 | if( p->iReg ){ |
| 78530 | cacheEntryClear(pParse, p); |
| 78531 | p->iReg = 0; |
| 78532 | } |
| @@ -79659,11 +79723,21 @@ | |
| 79659 | if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){ |
| 79660 | sqlite3ExprCodeAtInit(pParse, pExpr, target+i, 0); |
| 79661 | }else{ |
| 79662 | int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); |
| 79663 | if( inReg!=target+i ){ |
| 79664 | sqlite3VdbeAddOp2(pParse->pVdbe, copyOp, inReg, target+i); |
| 79665 | } |
| 79666 | } |
| 79667 | } |
| 79668 | return n; |
| 79669 | } |
| @@ -83083,14 +83157,10 @@ | |
| 83083 | { |
| 83084 | int rc = SQLITE_OK; |
| 83085 | if( pExpr ){ |
| 83086 | if( pExpr->op!=TK_ID ){ |
| 83087 | rc = sqlite3ResolveExprNames(pName, pExpr); |
| 83088 | if( rc==SQLITE_OK && !sqlite3ExprIsConstant(pExpr) ){ |
| 83089 | sqlite3ErrorMsg(pName->pParse, "invalid name: \"%s\"", pExpr->u.zToken); |
| 83090 | return SQLITE_ERROR; |
| 83091 | } |
| 83092 | }else{ |
| 83093 | pExpr->op = TK_STRING; |
| 83094 | } |
| 83095 | } |
| 83096 | return rc; |
| @@ -89324,11 +89394,10 @@ | |
| 89324 | Vdbe *v = pParse->pVdbe; |
| 89325 | int j; |
| 89326 | Table *pTab = pIdx->pTable; |
| 89327 | int regBase; |
| 89328 | int nCol; |
| 89329 | Index *pPk; |
| 89330 | |
| 89331 | if( piPartIdxLabel ){ |
| 89332 | if( pIdx->pPartIdxWhere ){ |
| 89333 | *piPartIdxLabel = sqlite3VdbeMakeLabel(v); |
| 89334 | pParse->iPartIdxTab = iDataCur; |
| @@ -89338,20 +89407,13 @@ | |
| 89338 | *piPartIdxLabel = 0; |
| 89339 | } |
| 89340 | } |
| 89341 | nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn; |
| 89342 | regBase = sqlite3GetTempRange(pParse, nCol); |
| 89343 | pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); |
| 89344 | for(j=0; j<nCol; j++){ |
| 89345 | i16 idx = pIdx->aiColumn[j]; |
| 89346 | if( pPk ) idx = sqlite3ColumnOfIndex(pPk, idx); |
| 89347 | if( idx<0 || idx==pTab->iPKey ){ |
| 89348 | sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regBase+j); |
| 89349 | }else{ |
| 89350 | sqlite3VdbeAddOp3(v, OP_Column, iDataCur, idx, regBase+j); |
| 89351 | sqlite3ColumnDefault(v, pTab, pIdx->aiColumn[j], -1); |
| 89352 | } |
| 89353 | } |
| 89354 | if( regOut ){ |
| 89355 | const char *zAff; |
| 89356 | if( pTab->pSelect |
| 89357 | || OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt) |
| @@ -93982,51 +94044,53 @@ | |
| 93982 | sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk, |
| 93983 | regIdx, pIdx->nKeyCol); |
| 93984 | |
| 93985 | /* Generate code to handle collisions */ |
| 93986 | regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField); |
| 93987 | if( HasRowid(pTab) ){ |
| 93988 | sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); |
| 93989 | /* Conflict only if the rowid of the existing index entry |
| 93990 | ** is different from old-rowid */ |
| 93991 | if( isUpdate ){ |
| 93992 | sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData); |
| 93993 | } |
| 93994 | }else{ |
| 93995 | int x; |
| 93996 | /* Extract the PRIMARY KEY from the end of the index entry and |
| 93997 | ** store it in registers regR..regR+nPk-1 */ |
| 93998 | if( (isUpdate || onError==OE_Replace) && pIdx!=pPk ){ |
| 93999 | for(i=0; i<pPk->nKeyCol; i++){ |
| 94000 | x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]); |
| 94001 | sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); |
| 94002 | VdbeComment((v, "%s.%s", pTab->zName, |
| 94003 | pTab->aCol[pPk->aiColumn[i]].zName)); |
| 94004 | } |
| 94005 | } |
| 94006 | if( isUpdate ){ |
| 94007 | /* If currently processing the PRIMARY KEY of a WITHOUT ROWID |
| 94008 | ** table, only conflict if the new PRIMARY KEY values are actually |
| 94009 | ** different from the old. |
| 94010 | ** |
| 94011 | ** For a UNIQUE index, only conflict if the PRIMARY KEY values |
| 94012 | ** of the matched index row are different from the original PRIMARY |
| 94013 | ** KEY values of this row before the update. */ |
| 94014 | int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; |
| 94015 | int op = OP_Ne; |
| 94016 | int regCmp = (pIdx->autoIndex==2 ? regIdx : regR); |
| 94017 | |
| 94018 | for(i=0; i<pPk->nKeyCol; i++){ |
| 94019 | char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]); |
| 94020 | x = pPk->aiColumn[i]; |
| 94021 | if( i==(pPk->nKeyCol-1) ){ |
| 94022 | addrJump = addrUniqueOk; |
| 94023 | op = OP_Eq; |
| 94024 | } |
| 94025 | sqlite3VdbeAddOp4(v, op, |
| 94026 | regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ |
| 94027 | ); |
| 94028 | } |
| 94029 | } |
| 94030 | } |
| 94031 | |
| 94032 | /* Generate code that executes if the new index entry is not unique */ |
| @@ -99722,11 +99786,10 @@ | |
| 99722 | } |
| 99723 | }else if( eDest!=SRT_Exists ){ |
| 99724 | /* If the destination is an EXISTS(...) expression, the actual |
| 99725 | ** values returned by the SELECT are not required. |
| 99726 | */ |
| 99727 | sqlite3ExprCacheClear(pParse); |
| 99728 | sqlite3ExprCodeExprList(pParse, pEList, regResult, |
| 99729 | (eDest==SRT_Output)?SQLITE_ECEL_DUP:0); |
| 99730 | } |
| 99731 | nColumn = nResultCol; |
| 99732 | |
| @@ -100689,11 +100752,11 @@ | |
| 100689 | ** If an error occurs, return NULL and leave a message in pParse. |
| 100690 | */ |
| 100691 | SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){ |
| 100692 | Vdbe *v = pParse->pVdbe; |
| 100693 | if( v==0 ){ |
| 100694 | v = pParse->pVdbe = sqlite3VdbeCreate(pParse->db); |
| 100695 | #ifndef SQLITE_OMIT_TRACE |
| 100696 | if( v ){ |
| 100697 | sqlite3VdbeAddOp0(v, OP_Trace); |
| 100698 | } |
| 100699 | #endif |
| @@ -102947,18 +103010,27 @@ | |
| 102947 | */ |
| 102948 | static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ |
| 102949 | Vdbe *v = pParse->pVdbe; |
| 102950 | int i; |
| 102951 | struct AggInfo_func *pFunc; |
| 102952 | if( pAggInfo->nFunc+pAggInfo->nColumn==0 ){ |
| 102953 | return; |
| 102954 | } |
| 102955 | for(i=0; i<pAggInfo->nColumn; i++){ |
| 102956 | sqlite3VdbeAddOp2(v, OP_Null, 0, pAggInfo->aCol[i].iMem); |
| 102957 | } |
| 102958 | for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){ |
| 102959 | sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem); |
| 102960 | if( pFunc->iDistinct>=0 ){ |
| 102961 | Expr *pE = pFunc->pExpr; |
| 102962 | assert( !ExprHasProperty(pE, EP_xIsSelect) ); |
| 102963 | if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ |
| 102964 | sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " |
| @@ -103000,11 +103072,10 @@ | |
| 103000 | int addrHitTest = 0; |
| 103001 | struct AggInfo_func *pF; |
| 103002 | struct AggInfo_col *pC; |
| 103003 | |
| 103004 | pAggInfo->directMode = 1; |
| 103005 | sqlite3ExprCacheClear(pParse); |
| 103006 | for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){ |
| 103007 | int nArg; |
| 103008 | int addrNext = 0; |
| 103009 | int regAgg; |
| 103010 | ExprList *pList = pF->pExpr->x.pList; |
| @@ -103533,10 +103604,11 @@ | |
| 103533 | */ |
| 103534 | memset(&sNC, 0, sizeof(sNC)); |
| 103535 | sNC.pParse = pParse; |
| 103536 | sNC.pSrcList = pTabList; |
| 103537 | sNC.pAggInfo = &sAggInfo; |
| 103538 | sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0; |
| 103539 | sAggInfo.pGroupBy = pGroupBy; |
| 103540 | sqlite3ExprAnalyzeAggList(&sNC, pEList); |
| 103541 | sqlite3ExprAnalyzeAggList(&sNC, pOrderBy); |
| 103542 | if( pHaving ){ |
| @@ -103547,10 +103619,11 @@ | |
| 103547 | assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) ); |
| 103548 | sNC.ncFlags |= NC_InAggFunc; |
| 103549 | sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList); |
| 103550 | sNC.ncFlags &= ~NC_InAggFunc; |
| 103551 | } |
| 103552 | if( db->mallocFailed ) goto select_end; |
| 103553 | |
| 103554 | /* Processing for aggregates with GROUP BY is very different and |
| 103555 | ** much more complex than aggregates without a GROUP BY. |
| 103556 | */ |
| @@ -105451,11 +105524,11 @@ | |
| 105451 | pCol->affinity, &pValue); |
| 105452 | if( pValue ){ |
| 105453 | sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM); |
| 105454 | } |
| 105455 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 105456 | if( iReg>=0 && pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ |
| 105457 | sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); |
| 105458 | } |
| 105459 | #endif |
| 105460 | } |
| 105461 | } |
| @@ -105875,14 +105948,14 @@ | |
| 105875 | ** be used eliminates some redundant opcodes. |
| 105876 | */ |
| 105877 | newmask = sqlite3TriggerColmask( |
| 105878 | pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError |
| 105879 | ); |
| 105880 | sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1); |
| 105881 | for(i=0; i<pTab->nCol; i++){ |
| 105882 | if( i==pTab->iPKey ){ |
| 105883 | /*sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);*/ |
| 105884 | }else{ |
| 105885 | j = aXRef[i]; |
| 105886 | if( j>=0 ){ |
| 105887 | sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); |
| 105888 | }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<<i)) ){ |
| @@ -105892,10 +105965,12 @@ | |
| 105892 | ** a new.* reference in a trigger program. |
| 105893 | */ |
| 105894 | testcase( i==31 ); |
| 105895 | testcase( i==32 ); |
| 105896 | sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i); |
| 105897 | } |
| 105898 | } |
| 105899 | } |
| 105900 | |
| 105901 | /* Fire any BEFORE UPDATE triggers. This happens before constraints are |
| @@ -112004,10 +112079,11 @@ | |
| 112004 | */ |
| 112005 | if( pTerm==0 |
| 112006 | && saved_nEq==saved_nSkip |
| 112007 | && saved_nEq+1<pProbe->nKeyCol |
| 112008 | && pProbe->aiRowEst[saved_nEq+1]>=18 /* TUNING: Minimum for skip-scan */ |
| 112009 | ){ |
| 112010 | LogEst nIter; |
| 112011 | pNew->u.btree.nEq++; |
| 112012 | pNew->u.btree.nSkip++; |
| 112013 | pNew->aLTerm[pNew->nLTerm++] = 0; |
| @@ -119722,12 +119798,11 @@ | |
| 119722 | |
| 119723 | /* |
| 119724 | ** Return a static string containing the name corresponding to the error code |
| 119725 | ** specified in the argument. |
| 119726 | */ |
| 119727 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \ |
| 119728 | defined(SQLITE_DEBUG_OS_TRACE) |
| 119729 | SQLITE_PRIVATE const char *sqlite3ErrName(int rc){ |
| 119730 | const char *zName = 0; |
| 119731 | int i, origRc = rc; |
| 119732 | for(i=0; i<2 && zName==0; i++, rc &= 0xff){ |
| 119733 | switch( rc ){ |
| @@ -121300,10 +121375,12 @@ | |
| 121300 | #ifdef SQLITE_DEFAULT_LOCKING_MODE |
| 121301 | db->dfltLockMode = SQLITE_DEFAULT_LOCKING_MODE; |
| 121302 | sqlite3PagerLockingMode(sqlite3BtreePager(db->aDb[0].pBt), |
| 121303 | SQLITE_DEFAULT_LOCKING_MODE); |
| 121304 | #endif |
| 121305 | |
| 121306 | /* Enable the lookaside-malloc subsystem */ |
| 121307 | setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside, |
| 121308 | sqlite3GlobalConfig.nLookaside); |
| 121309 | |
| @@ -131632,61 +131709,74 @@ | |
| 131632 | } |
| 131633 | |
| 131634 | /* Step 2 */ |
| 131635 | switch( z[1] ){ |
| 131636 | case 'a': |
| 131637 | stem(&z, "lanoita", "ate", m_gt_0) || |
| 131638 | stem(&z, "lanoit", "tion", m_gt_0); |
| 131639 | break; |
| 131640 | case 'c': |
| 131641 | stem(&z, "icne", "ence", m_gt_0) || |
| 131642 | stem(&z, "icna", "ance", m_gt_0); |
| 131643 | break; |
| 131644 | case 'e': |
| 131645 | stem(&z, "rezi", "ize", m_gt_0); |
| 131646 | break; |
| 131647 | case 'g': |
| 131648 | stem(&z, "igol", "log", m_gt_0); |
| 131649 | break; |
| 131650 | case 'l': |
| 131651 | stem(&z, "ilb", "ble", m_gt_0) || |
| 131652 | stem(&z, "illa", "al", m_gt_0) || |
| 131653 | stem(&z, "iltne", "ent", m_gt_0) || |
| 131654 | stem(&z, "ile", "e", m_gt_0) || |
| 131655 | stem(&z, "ilsuo", "ous", m_gt_0); |
| 131656 | break; |
| 131657 | case 'o': |
| 131658 | stem(&z, "noitazi", "ize", m_gt_0) || |
| 131659 | stem(&z, "noita", "ate", m_gt_0) || |
| 131660 | stem(&z, "rota", "ate", m_gt_0); |
| 131661 | break; |
| 131662 | case 's': |
| 131663 | stem(&z, "msila", "al", m_gt_0) || |
| 131664 | stem(&z, "ssenevi", "ive", m_gt_0) || |
| 131665 | stem(&z, "ssenluf", "ful", m_gt_0) || |
| 131666 | stem(&z, "ssensuo", "ous", m_gt_0); |
| 131667 | break; |
| 131668 | case 't': |
| 131669 | stem(&z, "itila", "al", m_gt_0) || |
| 131670 | stem(&z, "itivi", "ive", m_gt_0) || |
| 131671 | stem(&z, "itilib", "ble", m_gt_0); |
| 131672 | break; |
| 131673 | } |
| 131674 | |
| 131675 | /* Step 3 */ |
| 131676 | switch( z[0] ){ |
| 131677 | case 'e': |
| 131678 | stem(&z, "etaci", "ic", m_gt_0) || |
| 131679 | stem(&z, "evita", "", m_gt_0) || |
| 131680 | stem(&z, "ezila", "al", m_gt_0); |
| 131681 | break; |
| 131682 | case 'i': |
| 131683 | stem(&z, "itici", "ic", m_gt_0); |
| 131684 | break; |
| 131685 | case 'l': |
| 131686 | stem(&z, "laci", "ic", m_gt_0) || |
| 131687 | stem(&z, "luf", "", m_gt_0); |
| 131688 | break; |
| 131689 | case 's': |
| 131690 | stem(&z, "ssen", "", m_gt_0); |
| 131691 | break; |
| 131692 | } |
| @@ -131723,13 +131813,15 @@ | |
| 131723 | if( z[2]=='a' ){ |
| 131724 | if( m_gt_1(z+3) ){ |
| 131725 | z += 3; |
| 131726 | } |
| 131727 | }else if( z[2]=='e' ){ |
| 131728 | stem(&z, "tneme", "", m_gt_1) || |
| 131729 | stem(&z, "tnem", "", m_gt_1) || |
| 131730 | stem(&z, "tne", "", m_gt_1); |
| 131731 | } |
| 131732 | } |
| 131733 | break; |
| 131734 | case 'o': |
| 131735 | if( z[0]=='u' ){ |
| @@ -131744,12 +131836,13 @@ | |
| 131744 | if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){ |
| 131745 | z += 3; |
| 131746 | } |
| 131747 | break; |
| 131748 | case 't': |
| 131749 | stem(&z, "eta", "", m_gt_1) || |
| 131750 | stem(&z, "iti", "", m_gt_1); |
| 131751 | break; |
| 131752 | case 'u': |
| 131753 | if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){ |
| 131754 | z += 3; |
| 131755 | } |
| 131756 |
| --- 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 | ** |
| @@ -9098,11 +9098,11 @@ | |
| 9098 | #define OP_String 25 /* synopsis: r[P2]='P4' (len=P1) */ |
| 9099 | #define OP_Null 26 /* synopsis: r[P2..P3]=NULL */ |
| 9100 | #define OP_Blob 27 /* synopsis: r[P2]=P4 (len=P1) */ |
| 9101 | #define OP_Variable 28 /* synopsis: r[P2]=parameter(P1,P4) */ |
| 9102 | #define OP_Move 29 /* synopsis: r[P2@P3]=r[P1@P3] */ |
| 9103 | #define OP_Copy 30 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ |
| 9104 | #define OP_SCopy 31 /* synopsis: r[P2]=r[P1] */ |
| 9105 | #define OP_ResultRow 32 /* synopsis: output=r[P1@P2] */ |
| 9106 | #define OP_CollSeq 33 |
| 9107 | #define OP_AddImm 34 /* synopsis: r[P1]=r[P1]+P2 */ |
| 9108 | #define OP_MustBeInt 35 |
| @@ -9263,11 +9263,11 @@ | |
| 9263 | |
| 9264 | /* |
| 9265 | ** Prototypes for the VDBE interface. See comments on the implementation |
| 9266 | ** for a description of what each of these routines does. |
| 9267 | */ |
| 9268 | SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse*); |
| 9269 | SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); |
| 9270 | SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); |
| 9271 | SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); |
| 9272 | SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); |
| 9273 | SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); |
| @@ -11003,10 +11003,11 @@ | |
| 11003 | u8 useSortingIdx; /* In direct mode, reference the sorting index rather |
| 11004 | ** than the source table */ |
| 11005 | int sortingIdx; /* Cursor number of the sorting index */ |
| 11006 | int sortingIdxPTab; /* Cursor number of pseudo-table */ |
| 11007 | int nSortingColumn; /* Number of columns in the sorting index */ |
| 11008 | int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */ |
| 11009 | ExprList *pGroupBy; /* The group by clause */ |
| 11010 | struct AggInfo_col { /* For each column used in source tables */ |
| 11011 | Table *pTab; /* Source table */ |
| 11012 | int iTable; /* Cursor number of the source table */ |
| 11013 | int iColumn; /* Column number within the source table */ |
| @@ -12452,12 +12453,11 @@ | |
| 12453 | SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...); |
| 12454 | SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); |
| 12455 | SQLITE_PRIVATE u8 sqlite3HexToInt(int h); |
| 12456 | SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); |
| 12457 | |
| 12458 | #if defined(SQLITE_TEST) |
| 12459 | SQLITE_PRIVATE const char *sqlite3ErrName(int); |
| 12460 | #endif |
| 12461 | |
| 12462 | SQLITE_PRIVATE const char *sqlite3ErrStr(int); |
| 12463 | SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); |
| @@ -13776,10 +13776,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 */ |
| @@ -15443,11 +15446,25 @@ | |
| 15446 | ** really care if the VFS receives and understands the information since it |
| 15447 | ** is only a hint and can be safely ignored. The sqlite3OsFileControlHint() |
| 15448 | ** routine has no return value since the return value would be meaningless. |
| 15449 | */ |
| 15450 | SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ |
| 15451 | #ifdef SQLITE_TEST |
| 15452 | if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){ |
| 15453 | /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite |
| 15454 | ** is using a regular VFS, it is called after the corresponding |
| 15455 | ** transaction has been committed. Injecting a fault at this point |
| 15456 | ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM |
| 15457 | ** but the transaction is committed anyway. |
| 15458 | ** |
| 15459 | ** The core must call OsFileControl() though, not OsFileControlHint(), |
| 15460 | ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably |
| 15461 | ** means the commit really has failed and an error should be returned |
| 15462 | ** to the user. */ |
| 15463 | DO_OS_MALLOC_TEST(id); |
| 15464 | } |
| 15465 | #endif |
| 15466 | return id->pMethods->xFileControl(id, op, pArg); |
| 15467 | } |
| 15468 | SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){ |
| 15469 | (void)id->pMethods->xFileControl(id, op, pArg); |
| 15470 | } |
| @@ -23098,11 +23115,11 @@ | |
| 23115 | /* 25 */ "String" OpHelp("r[P2]='P4' (len=P1)"), |
| 23116 | /* 26 */ "Null" OpHelp("r[P2..P3]=NULL"), |
| 23117 | /* 27 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), |
| 23118 | /* 28 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), |
| 23119 | /* 29 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), |
| 23120 | /* 30 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), |
| 23121 | /* 31 */ "SCopy" OpHelp("r[P2]=r[P1]"), |
| 23122 | /* 32 */ "ResultRow" OpHelp("output=r[P1@P2]"), |
| 23123 | /* 33 */ "CollSeq" OpHelp(""), |
| 23124 | /* 34 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), |
| 23125 | /* 35 */ "MustBeInt" OpHelp(""), |
| @@ -61223,11 +61240,12 @@ | |
| 61240 | */ |
| 61241 | |
| 61242 | /* |
| 61243 | ** Create a new virtual database engine. |
| 61244 | */ |
| 61245 | SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){ |
| 61246 | sqlite3 *db = pParse->db; |
| 61247 | Vdbe *p; |
| 61248 | p = sqlite3DbMallocZero(db, sizeof(Vdbe) ); |
| 61249 | if( p==0 ) return 0; |
| 61250 | p->db = db; |
| 61251 | if( db->pVdbe ){ |
| @@ -61235,10 +61253,13 @@ | |
| 61253 | } |
| 61254 | p->pNext = db->pVdbe; |
| 61255 | p->pPrev = 0; |
| 61256 | db->pVdbe = p; |
| 61257 | p->magic = VDBE_MAGIC_INIT; |
| 61258 | #if SQLITE_DEBUG |
| 61259 | p->pParse = pParse; |
| 61260 | #endif |
| 61261 | return p; |
| 61262 | } |
| 61263 | |
| 61264 | /* |
| 61265 | ** Remember the SQL string for a prepared statement. |
| @@ -61354,10 +61375,19 @@ | |
| 61375 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 61376 | pOp->zComment = 0; |
| 61377 | #endif |
| 61378 | #ifdef SQLITE_DEBUG |
| 61379 | if( p->db->flags & SQLITE_VdbeAddopTrace ){ |
| 61380 | int jj, kk; |
| 61381 | Parse *pParse = p->pParse; |
| 61382 | for(jj=kk=0; jj<SQLITE_N_COLCACHE; jj++){ |
| 61383 | struct yColCache *x = pParse->aColCache + jj; |
| 61384 | if( x->iLevel>pParse->iCacheLevel || x->iReg==0 ) continue; |
| 61385 | printf(" r[%d]={%d:%d}", x->iReg, x->iTable, x->iColumn); |
| 61386 | kk++; |
| 61387 | } |
| 61388 | if( kk ) printf("\n"); |
| 61389 | sqlite3VdbePrintOp(0, i, &p->aOp[i]); |
| 61390 | test_addop_breakpoint(); |
| 61391 | } |
| 61392 | #endif |
| 61393 | #ifdef VDBE_PROFILE |
| @@ -62075,11 +62105,21 @@ | |
| 62105 | if( c=='4' ) return pOp->p4.i; |
| 62106 | return pOp->p5; |
| 62107 | } |
| 62108 | |
| 62109 | /* |
| 62110 | ** Compute a string for the "comment" field of a VDBE opcode listing. |
| 62111 | ** |
| 62112 | ** The Synopsis: field in comments in the vdbe.c source file gets converted |
| 62113 | ** to an extra string that is appended to the sqlite3OpcodeName(). In the |
| 62114 | ** absence of other comments, this synopsis becomes the comment on the opcode. |
| 62115 | ** Some translation occurs: |
| 62116 | ** |
| 62117 | ** "PX" -> "r[X]" |
| 62118 | ** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1 |
| 62119 | ** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0 |
| 62120 | ** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x |
| 62121 | */ |
| 62122 | static int displayComment( |
| 62123 | const Op *pOp, /* The opcode to be commented */ |
| 62124 | const char *zP4, /* Previously obtained value for P4 */ |
| 62125 | char *zTemp, /* Write result here */ |
| @@ -62109,11 +62149,17 @@ | |
| 62149 | sqlite3_snprintf(nTemp-jj, zTemp+jj, "%d", v1); |
| 62150 | if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){ |
| 62151 | ii += 3; |
| 62152 | jj += sqlite3Strlen30(zTemp+jj); |
| 62153 | v2 = translateP(zSynopsis[ii], pOp); |
| 62154 | if( strncmp(zSynopsis+ii+1,"+1",2)==0 ){ |
| 62155 | ii += 2; |
| 62156 | v2++; |
| 62157 | } |
| 62158 | if( v2>1 ){ |
| 62159 | sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1); |
| 62160 | } |
| 62161 | }else if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){ |
| 62162 | ii += 4; |
| 62163 | } |
| 62164 | } |
| 62165 | jj += sqlite3Strlen30(zTemp+jj); |
| @@ -62341,10 +62387,13 @@ | |
| 62387 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 62388 | displayComment(pOp, zP4, zCom, sizeof(zCom)); |
| 62389 | #else |
| 62390 | zCom[0] = 0 |
| 62391 | #endif |
| 62392 | /* NB: The sqlite3OpcodeName() function is implemented by code created |
| 62393 | ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the |
| 62394 | ** information from the vdbe.c source text */ |
| 62395 | fprintf(pOut, zFormat1, pc, |
| 62396 | sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5, |
| 62397 | zCom |
| 62398 | ); |
| 62399 | fflush(pOut); |
| @@ -67401,11 +67450,11 @@ | |
| 67450 | }while( n-- ); |
| 67451 | break; |
| 67452 | } |
| 67453 | |
| 67454 | /* Opcode: Copy P1 P2 P3 * * |
| 67455 | ** Synopsis: r[P2@P3+1]=r[P1@P3+1] |
| 67456 | ** |
| 67457 | ** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. |
| 67458 | ** |
| 67459 | ** This instruction makes a deep copy of the value. A duplicate |
| 67460 | ** is made of any string or blob constant. See also OP_SCopy. |
| @@ -68744,11 +68793,11 @@ | |
| 68793 | /* This is the common case where the desired content fits on the original |
| 68794 | ** page - where the content is not on an overflow page */ |
| 68795 | VdbeMemRelease(pDest); |
| 68796 | sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], aType[p2], pDest); |
| 68797 | }else{ |
| 68798 | /* This branch happens only when content is on overflow pages */ |
| 68799 | t = aType[p2]; |
| 68800 | if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 |
| 68801 | && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) |
| 68802 | || (len = sqlite3VdbeSerialTypeLen(t))==0 |
| 68803 | ){ |
| @@ -72862,11 +72911,11 @@ | |
| 72911 | sqlite3BtreeLeaveAll(db); |
| 72912 | goto blob_open_out; |
| 72913 | } |
| 72914 | } |
| 72915 | |
| 72916 | pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse); |
| 72917 | assert( pBlob->pStmt || db->mallocFailed ); |
| 72918 | if( pBlob->pStmt ){ |
| 72919 | Vdbe *v = (Vdbe *)pBlob->pStmt; |
| 72920 | int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 72921 | |
| @@ -78416,10 +78465,15 @@ | |
| 78465 | ** added to the column cache after this call are removed when the |
| 78466 | ** corresponding pop occurs. |
| 78467 | */ |
| 78468 | SQLITE_PRIVATE void sqlite3ExprCachePush(Parse *pParse){ |
| 78469 | pParse->iCacheLevel++; |
| 78470 | #ifdef SQLITE_DEBUG |
| 78471 | if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ |
| 78472 | printf("PUSH to %d\n", pParse->iCacheLevel); |
| 78473 | } |
| 78474 | #endif |
| 78475 | } |
| 78476 | |
| 78477 | /* |
| 78478 | ** Remove from the column cache any entries that were added since the |
| 78479 | ** the previous N Push operations. In other words, restore the cache |
| @@ -78429,10 +78483,15 @@ | |
| 78483 | int i; |
| 78484 | struct yColCache *p; |
| 78485 | assert( N>0 ); |
| 78486 | assert( pParse->iCacheLevel>=N ); |
| 78487 | pParse->iCacheLevel -= N; |
| 78488 | #ifdef SQLITE_DEBUG |
| 78489 | if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ |
| 78490 | printf("POP to %d\n", pParse->iCacheLevel); |
| 78491 | } |
| 78492 | #endif |
| 78493 | for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ |
| 78494 | if( p->iReg && p->iLevel>pParse->iCacheLevel ){ |
| 78495 | cacheEntryClear(pParse, p); |
| 78496 | p->iReg = 0; |
| 78497 | } |
| @@ -78523,10 +78582,15 @@ | |
| 78582 | */ |
| 78583 | SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse *pParse){ |
| 78584 | int i; |
| 78585 | struct yColCache *p; |
| 78586 | |
| 78587 | #if SQLITE_DEBUG |
| 78588 | if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ |
| 78589 | printf("CLEAR\n"); |
| 78590 | } |
| 78591 | #endif |
| 78592 | for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ |
| 78593 | if( p->iReg ){ |
| 78594 | cacheEntryClear(pParse, p); |
| 78595 | p->iReg = 0; |
| 78596 | } |
| @@ -79659,11 +79723,21 @@ | |
| 79723 | if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){ |
| 79724 | sqlite3ExprCodeAtInit(pParse, pExpr, target+i, 0); |
| 79725 | }else{ |
| 79726 | int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); |
| 79727 | if( inReg!=target+i ){ |
| 79728 | VdbeOp *pOp; |
| 79729 | Vdbe *v = pParse->pVdbe; |
| 79730 | if( copyOp==OP_Copy |
| 79731 | && (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy |
| 79732 | && pOp->p1+pOp->p3+1==inReg |
| 79733 | && pOp->p2+pOp->p3+1==target+i |
| 79734 | ){ |
| 79735 | pOp->p3++; |
| 79736 | }else{ |
| 79737 | sqlite3VdbeAddOp2(v, copyOp, inReg, target+i); |
| 79738 | } |
| 79739 | } |
| 79740 | } |
| 79741 | } |
| 79742 | return n; |
| 79743 | } |
| @@ -83083,14 +83157,10 @@ | |
| 83157 | { |
| 83158 | int rc = SQLITE_OK; |
| 83159 | if( pExpr ){ |
| 83160 | if( pExpr->op!=TK_ID ){ |
| 83161 | rc = sqlite3ResolveExprNames(pName, pExpr); |
| 83162 | }else{ |
| 83163 | pExpr->op = TK_STRING; |
| 83164 | } |
| 83165 | } |
| 83166 | return rc; |
| @@ -89324,11 +89394,10 @@ | |
| 89394 | Vdbe *v = pParse->pVdbe; |
| 89395 | int j; |
| 89396 | Table *pTab = pIdx->pTable; |
| 89397 | int regBase; |
| 89398 | int nCol; |
| 89399 | |
| 89400 | if( piPartIdxLabel ){ |
| 89401 | if( pIdx->pPartIdxWhere ){ |
| 89402 | *piPartIdxLabel = sqlite3VdbeMakeLabel(v); |
| 89403 | pParse->iPartIdxTab = iDataCur; |
| @@ -89338,20 +89407,13 @@ | |
| 89407 | *piPartIdxLabel = 0; |
| 89408 | } |
| 89409 | } |
| 89410 | nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn; |
| 89411 | regBase = sqlite3GetTempRange(pParse, nCol); |
| 89412 | for(j=0; j<nCol; j++){ |
| 89413 | sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j], |
| 89414 | regBase+j); |
| 89415 | } |
| 89416 | if( regOut ){ |
| 89417 | const char *zAff; |
| 89418 | if( pTab->pSelect |
| 89419 | || OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt) |
| @@ -93982,51 +94044,53 @@ | |
| 94044 | sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk, |
| 94045 | regIdx, pIdx->nKeyCol); |
| 94046 | |
| 94047 | /* Generate code to handle collisions */ |
| 94048 | regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField); |
| 94049 | if( isUpdate || onError==OE_Replace ){ |
| 94050 | if( HasRowid(pTab) ){ |
| 94051 | sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); |
| 94052 | /* Conflict only if the rowid of the existing index entry |
| 94053 | ** is different from old-rowid */ |
| 94054 | if( isUpdate ){ |
| 94055 | sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData); |
| 94056 | } |
| 94057 | }else{ |
| 94058 | int x; |
| 94059 | /* Extract the PRIMARY KEY from the end of the index entry and |
| 94060 | ** store it in registers regR..regR+nPk-1 */ |
| 94061 | if( pIdx!=pPk ){ |
| 94062 | for(i=0; i<pPk->nKeyCol; i++){ |
| 94063 | x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]); |
| 94064 | sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); |
| 94065 | VdbeComment((v, "%s.%s", pTab->zName, |
| 94066 | pTab->aCol[pPk->aiColumn[i]].zName)); |
| 94067 | } |
| 94068 | } |
| 94069 | if( isUpdate ){ |
| 94070 | /* If currently processing the PRIMARY KEY of a WITHOUT ROWID |
| 94071 | ** table, only conflict if the new PRIMARY KEY values are actually |
| 94072 | ** different from the old. |
| 94073 | ** |
| 94074 | ** For a UNIQUE index, only conflict if the PRIMARY KEY values |
| 94075 | ** of the matched index row are different from the original PRIMARY |
| 94076 | ** KEY values of this row before the update. */ |
| 94077 | int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; |
| 94078 | int op = OP_Ne; |
| 94079 | int regCmp = (pIdx->autoIndex==2 ? regIdx : regR); |
| 94080 | |
| 94081 | for(i=0; i<pPk->nKeyCol; i++){ |
| 94082 | char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]); |
| 94083 | x = pPk->aiColumn[i]; |
| 94084 | if( i==(pPk->nKeyCol-1) ){ |
| 94085 | addrJump = addrUniqueOk; |
| 94086 | op = OP_Eq; |
| 94087 | } |
| 94088 | sqlite3VdbeAddOp4(v, op, |
| 94089 | regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ |
| 94090 | ); |
| 94091 | } |
| 94092 | } |
| 94093 | } |
| 94094 | } |
| 94095 | |
| 94096 | /* Generate code that executes if the new index entry is not unique */ |
| @@ -99722,11 +99786,10 @@ | |
| 99786 | } |
| 99787 | }else if( eDest!=SRT_Exists ){ |
| 99788 | /* If the destination is an EXISTS(...) expression, the actual |
| 99789 | ** values returned by the SELECT are not required. |
| 99790 | */ |
| 99791 | sqlite3ExprCodeExprList(pParse, pEList, regResult, |
| 99792 | (eDest==SRT_Output)?SQLITE_ECEL_DUP:0); |
| 99793 | } |
| 99794 | nColumn = nResultCol; |
| 99795 | |
| @@ -100689,11 +100752,11 @@ | |
| 100752 | ** If an error occurs, return NULL and leave a message in pParse. |
| 100753 | */ |
| 100754 | SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){ |
| 100755 | Vdbe *v = pParse->pVdbe; |
| 100756 | if( v==0 ){ |
| 100757 | v = pParse->pVdbe = sqlite3VdbeCreate(pParse); |
| 100758 | #ifndef SQLITE_OMIT_TRACE |
| 100759 | if( v ){ |
| 100760 | sqlite3VdbeAddOp0(v, OP_Trace); |
| 100761 | } |
| 100762 | #endif |
| @@ -102947,18 +103010,27 @@ | |
| 103010 | */ |
| 103011 | static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ |
| 103012 | Vdbe *v = pParse->pVdbe; |
| 103013 | int i; |
| 103014 | struct AggInfo_func *pFunc; |
| 103015 | int nReg = pAggInfo->nFunc + pAggInfo->nColumn; |
| 103016 | if( nReg==0 ) return; |
| 103017 | #ifdef SQLITE_DEBUG |
| 103018 | /* Verify that all AggInfo registers are within the range specified by |
| 103019 | ** AggInfo.mnReg..AggInfo.mxReg */ |
| 103020 | assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 ); |
| 103021 | for(i=0; i<pAggInfo->nColumn; i++){ |
| 103022 | assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg |
| 103023 | && pAggInfo->aCol[i].iMem<=pAggInfo->mxReg ); |
| 103024 | } |
| 103025 | for(i=0; i<pAggInfo->nFunc; i++){ |
| 103026 | assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg |
| 103027 | && pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg ); |
| 103028 | } |
| 103029 | #endif |
| 103030 | sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg); |
| 103031 | for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){ |
| 103032 | if( pFunc->iDistinct>=0 ){ |
| 103033 | Expr *pE = pFunc->pExpr; |
| 103034 | assert( !ExprHasProperty(pE, EP_xIsSelect) ); |
| 103035 | if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ |
| 103036 | sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " |
| @@ -103000,11 +103072,10 @@ | |
| 103072 | int addrHitTest = 0; |
| 103073 | struct AggInfo_func *pF; |
| 103074 | struct AggInfo_col *pC; |
| 103075 | |
| 103076 | pAggInfo->directMode = 1; |
| 103077 | for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){ |
| 103078 | int nArg; |
| 103079 | int addrNext = 0; |
| 103080 | int regAgg; |
| 103081 | ExprList *pList = pF->pExpr->x.pList; |
| @@ -103533,10 +103604,11 @@ | |
| 103604 | */ |
| 103605 | memset(&sNC, 0, sizeof(sNC)); |
| 103606 | sNC.pParse = pParse; |
| 103607 | sNC.pSrcList = pTabList; |
| 103608 | sNC.pAggInfo = &sAggInfo; |
| 103609 | sAggInfo.mnReg = pParse->nMem+1; |
| 103610 | sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0; |
| 103611 | sAggInfo.pGroupBy = pGroupBy; |
| 103612 | sqlite3ExprAnalyzeAggList(&sNC, pEList); |
| 103613 | sqlite3ExprAnalyzeAggList(&sNC, pOrderBy); |
| 103614 | if( pHaving ){ |
| @@ -103547,10 +103619,11 @@ | |
| 103619 | assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) ); |
| 103620 | sNC.ncFlags |= NC_InAggFunc; |
| 103621 | sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList); |
| 103622 | sNC.ncFlags &= ~NC_InAggFunc; |
| 103623 | } |
| 103624 | sAggInfo.mxReg = pParse->nMem; |
| 103625 | if( db->mallocFailed ) goto select_end; |
| 103626 | |
| 103627 | /* Processing for aggregates with GROUP BY is very different and |
| 103628 | ** much more complex than aggregates without a GROUP BY. |
| 103629 | */ |
| @@ -105451,11 +105524,11 @@ | |
| 105524 | pCol->affinity, &pValue); |
| 105525 | if( pValue ){ |
| 105526 | sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM); |
| 105527 | } |
| 105528 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 105529 | if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ |
| 105530 | sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); |
| 105531 | } |
| 105532 | #endif |
| 105533 | } |
| 105534 | } |
| @@ -105875,14 +105948,14 @@ | |
| 105948 | ** be used eliminates some redundant opcodes. |
| 105949 | */ |
| 105950 | newmask = sqlite3TriggerColmask( |
| 105951 | pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError |
| 105952 | ); |
| 105953 | /*sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);*/ |
| 105954 | for(i=0; i<pTab->nCol; i++){ |
| 105955 | if( i==pTab->iPKey ){ |
| 105956 | sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); |
| 105957 | }else{ |
| 105958 | j = aXRef[i]; |
| 105959 | if( j>=0 ){ |
| 105960 | sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); |
| 105961 | }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<<i)) ){ |
| @@ -105892,10 +105965,12 @@ | |
| 105965 | ** a new.* reference in a trigger program. |
| 105966 | */ |
| 105967 | testcase( i==31 ); |
| 105968 | testcase( i==32 ); |
| 105969 | sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i); |
| 105970 | }else{ |
| 105971 | sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); |
| 105972 | } |
| 105973 | } |
| 105974 | } |
| 105975 | |
| 105976 | /* Fire any BEFORE UPDATE triggers. This happens before constraints are |
| @@ -112004,10 +112079,11 @@ | |
| 112079 | */ |
| 112080 | if( pTerm==0 |
| 112081 | && saved_nEq==saved_nSkip |
| 112082 | && saved_nEq+1<pProbe->nKeyCol |
| 112083 | && pProbe->aiRowEst[saved_nEq+1]>=18 /* TUNING: Minimum for skip-scan */ |
| 112084 | && (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK |
| 112085 | ){ |
| 112086 | LogEst nIter; |
| 112087 | pNew->u.btree.nEq++; |
| 112088 | pNew->u.btree.nSkip++; |
| 112089 | pNew->aLTerm[pNew->nLTerm++] = 0; |
| @@ -119722,12 +119798,11 @@ | |
| 119798 | |
| 119799 | /* |
| 119800 | ** Return a static string containing the name corresponding to the error code |
| 119801 | ** specified in the argument. |
| 119802 | */ |
| 119803 | #if defined(SQLITE_TEST) |
| 119804 | SQLITE_PRIVATE const char *sqlite3ErrName(int rc){ |
| 119805 | const char *zName = 0; |
| 119806 | int i, origRc = rc; |
| 119807 | for(i=0; i<2 && zName==0; i++, rc &= 0xff){ |
| 119808 | switch( rc ){ |
| @@ -121300,10 +121375,12 @@ | |
| 121375 | #ifdef SQLITE_DEFAULT_LOCKING_MODE |
| 121376 | db->dfltLockMode = SQLITE_DEFAULT_LOCKING_MODE; |
| 121377 | sqlite3PagerLockingMode(sqlite3BtreePager(db->aDb[0].pBt), |
| 121378 | SQLITE_DEFAULT_LOCKING_MODE); |
| 121379 | #endif |
| 121380 | |
| 121381 | if( rc ) sqlite3Error(db, rc, 0); |
| 121382 | |
| 121383 | /* Enable the lookaside-malloc subsystem */ |
| 121384 | setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside, |
| 121385 | sqlite3GlobalConfig.nLookaside); |
| 121386 | |
| @@ -131632,61 +131709,74 @@ | |
| 131709 | } |
| 131710 | |
| 131711 | /* Step 2 */ |
| 131712 | switch( z[1] ){ |
| 131713 | case 'a': |
| 131714 | if( !stem(&z, "lanoita", "ate", m_gt_0) ){ |
| 131715 | stem(&z, "lanoit", "tion", m_gt_0); |
| 131716 | } |
| 131717 | break; |
| 131718 | case 'c': |
| 131719 | if( !stem(&z, "icne", "ence", m_gt_0) ){ |
| 131720 | stem(&z, "icna", "ance", m_gt_0); |
| 131721 | } |
| 131722 | break; |
| 131723 | case 'e': |
| 131724 | stem(&z, "rezi", "ize", m_gt_0); |
| 131725 | break; |
| 131726 | case 'g': |
| 131727 | stem(&z, "igol", "log", m_gt_0); |
| 131728 | break; |
| 131729 | case 'l': |
| 131730 | if( !stem(&z, "ilb", "ble", m_gt_0) |
| 131731 | && !stem(&z, "illa", "al", m_gt_0) |
| 131732 | && !stem(&z, "iltne", "ent", m_gt_0) |
| 131733 | && !stem(&z, "ile", "e", m_gt_0) |
| 131734 | ){ |
| 131735 | stem(&z, "ilsuo", "ous", m_gt_0); |
| 131736 | } |
| 131737 | break; |
| 131738 | case 'o': |
| 131739 | if( !stem(&z, "noitazi", "ize", m_gt_0) |
| 131740 | && !stem(&z, "noita", "ate", m_gt_0) |
| 131741 | ){ |
| 131742 | stem(&z, "rota", "ate", m_gt_0); |
| 131743 | } |
| 131744 | break; |
| 131745 | case 's': |
| 131746 | if( !stem(&z, "msila", "al", m_gt_0) |
| 131747 | && !stem(&z, "ssenevi", "ive", m_gt_0) |
| 131748 | && !stem(&z, "ssenluf", "ful", m_gt_0) |
| 131749 | ){ |
| 131750 | stem(&z, "ssensuo", "ous", m_gt_0); |
| 131751 | } |
| 131752 | break; |
| 131753 | case 't': |
| 131754 | if( !stem(&z, "itila", "al", m_gt_0) |
| 131755 | && !stem(&z, "itivi", "ive", m_gt_0) |
| 131756 | ){ |
| 131757 | stem(&z, "itilib", "ble", m_gt_0); |
| 131758 | } |
| 131759 | break; |
| 131760 | } |
| 131761 | |
| 131762 | /* Step 3 */ |
| 131763 | switch( z[0] ){ |
| 131764 | case 'e': |
| 131765 | if( !stem(&z, "etaci", "ic", m_gt_0) |
| 131766 | && !stem(&z, "evita", "", m_gt_0) |
| 131767 | ){ |
| 131768 | stem(&z, "ezila", "al", m_gt_0); |
| 131769 | } |
| 131770 | break; |
| 131771 | case 'i': |
| 131772 | stem(&z, "itici", "ic", m_gt_0); |
| 131773 | break; |
| 131774 | case 'l': |
| 131775 | if( !stem(&z, "laci", "ic", m_gt_0) ){ |
| 131776 | stem(&z, "luf", "", m_gt_0); |
| 131777 | } |
| 131778 | break; |
| 131779 | case 's': |
| 131780 | stem(&z, "ssen", "", m_gt_0); |
| 131781 | break; |
| 131782 | } |
| @@ -131723,13 +131813,15 @@ | |
| 131813 | if( z[2]=='a' ){ |
| 131814 | if( m_gt_1(z+3) ){ |
| 131815 | z += 3; |
| 131816 | } |
| 131817 | }else if( z[2]=='e' ){ |
| 131818 | if( !stem(&z, "tneme", "", m_gt_1) |
| 131819 | && !stem(&z, "tnem", "", m_gt_1) |
| 131820 | ){ |
| 131821 | stem(&z, "tne", "", m_gt_1); |
| 131822 | } |
| 131823 | } |
| 131824 | } |
| 131825 | break; |
| 131826 | case 'o': |
| 131827 | if( z[0]=='u' ){ |
| @@ -131744,12 +131836,13 @@ | |
| 131836 | if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){ |
| 131837 | z += 3; |
| 131838 | } |
| 131839 | break; |
| 131840 | case 't': |
| 131841 | if( !stem(&z, "eta", "", m_gt_1) ){ |
| 131842 | stem(&z, "iti", "", m_gt_1); |
| 131843 | } |
| 131844 | break; |
| 131845 | case 'u': |
| 131846 | if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){ |
| 131847 | z += 3; |
| 131848 | } |
| 131849 |
+204
-111
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -135,11 +135,11 @@ | ||
| 135 | 135 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 136 | 136 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 137 | 137 | */ |
| 138 | 138 | #define SQLITE_VERSION "3.8.3" |
| 139 | 139 | #define SQLITE_VERSION_NUMBER 3008003 |
| 140 | -#define SQLITE_SOURCE_ID "2013-12-17 16:32:56 93121d3097a43997af3c0de65bd9bd7663845fa2" | |
| 140 | +#define SQLITE_SOURCE_ID "2013-12-23 11:33:32 25b8a1c9ba77df3b7c78cbce922cb593d661696d" | |
| 141 | 141 | |
| 142 | 142 | /* |
| 143 | 143 | ** CAPI3REF: Run-Time Library Version Numbers |
| 144 | 144 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 145 | 145 | ** |
| @@ -9098,11 +9098,11 @@ | ||
| 9098 | 9098 | #define OP_String 25 /* synopsis: r[P2]='P4' (len=P1) */ |
| 9099 | 9099 | #define OP_Null 26 /* synopsis: r[P2..P3]=NULL */ |
| 9100 | 9100 | #define OP_Blob 27 /* synopsis: r[P2]=P4 (len=P1) */ |
| 9101 | 9101 | #define OP_Variable 28 /* synopsis: r[P2]=parameter(P1,P4) */ |
| 9102 | 9102 | #define OP_Move 29 /* synopsis: r[P2@P3]=r[P1@P3] */ |
| 9103 | -#define OP_Copy 30 /* synopsis: r[P2@P3]=r[P1@P3] */ | |
| 9103 | +#define OP_Copy 30 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ | |
| 9104 | 9104 | #define OP_SCopy 31 /* synopsis: r[P2]=r[P1] */ |
| 9105 | 9105 | #define OP_ResultRow 32 /* synopsis: output=r[P1@P2] */ |
| 9106 | 9106 | #define OP_CollSeq 33 |
| 9107 | 9107 | #define OP_AddImm 34 /* synopsis: r[P1]=r[P1]+P2 */ |
| 9108 | 9108 | #define OP_MustBeInt 35 |
| @@ -9263,11 +9263,11 @@ | ||
| 9263 | 9263 | |
| 9264 | 9264 | /* |
| 9265 | 9265 | ** Prototypes for the VDBE interface. See comments on the implementation |
| 9266 | 9266 | ** for a description of what each of these routines does. |
| 9267 | 9267 | */ |
| 9268 | -SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3*); | |
| 9268 | +SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse*); | |
| 9269 | 9269 | SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); |
| 9270 | 9270 | SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); |
| 9271 | 9271 | SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); |
| 9272 | 9272 | SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); |
| 9273 | 9273 | SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); |
| @@ -11003,10 +11003,11 @@ | ||
| 11003 | 11003 | u8 useSortingIdx; /* In direct mode, reference the sorting index rather |
| 11004 | 11004 | ** than the source table */ |
| 11005 | 11005 | int sortingIdx; /* Cursor number of the sorting index */ |
| 11006 | 11006 | int sortingIdxPTab; /* Cursor number of pseudo-table */ |
| 11007 | 11007 | int nSortingColumn; /* Number of columns in the sorting index */ |
| 11008 | + int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */ | |
| 11008 | 11009 | ExprList *pGroupBy; /* The group by clause */ |
| 11009 | 11010 | struct AggInfo_col { /* For each column used in source tables */ |
| 11010 | 11011 | Table *pTab; /* Source table */ |
| 11011 | 11012 | int iTable; /* Cursor number of the source table */ |
| 11012 | 11013 | int iColumn; /* Column number within the source table */ |
| @@ -12452,12 +12453,11 @@ | ||
| 12452 | 12453 | SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...); |
| 12453 | 12454 | SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); |
| 12454 | 12455 | SQLITE_PRIVATE u8 sqlite3HexToInt(int h); |
| 12455 | 12456 | SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); |
| 12456 | 12457 | |
| 12457 | -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \ | |
| 12458 | - defined(SQLITE_DEBUG_OS_TRACE) | |
| 12458 | +#if defined(SQLITE_TEST) | |
| 12459 | 12459 | SQLITE_PRIVATE const char *sqlite3ErrName(int); |
| 12460 | 12460 | #endif |
| 12461 | 12461 | |
| 12462 | 12462 | SQLITE_PRIVATE const char *sqlite3ErrStr(int); |
| 12463 | 12463 | SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); |
| @@ -13776,10 +13776,13 @@ | ||
| 13776 | 13776 | Op *aOp; /* Space to hold the virtual machine's program */ |
| 13777 | 13777 | Mem *aMem; /* The memory locations */ |
| 13778 | 13778 | Mem **apArg; /* Arguments to currently executing user function */ |
| 13779 | 13779 | Mem *aColName; /* Column names to return */ |
| 13780 | 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 | |
| 13781 | 13784 | int nMem; /* Number of memory locations currently allocated */ |
| 13782 | 13785 | int nOp; /* Number of instructions in the program */ |
| 13783 | 13786 | int nOpAlloc; /* Number of slots allocated for aOp[] */ |
| 13784 | 13787 | int nLabel; /* Number of labels used */ |
| 13785 | 13788 | int *aLabel; /* Space to hold the labels */ |
| @@ -15443,11 +15446,25 @@ | ||
| 15443 | 15446 | ** really care if the VFS receives and understands the information since it |
| 15444 | 15447 | ** is only a hint and can be safely ignored. The sqlite3OsFileControlHint() |
| 15445 | 15448 | ** routine has no return value since the return value would be meaningless. |
| 15446 | 15449 | */ |
| 15447 | 15450 | SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ |
| 15448 | - DO_OS_MALLOC_TEST(id); | |
| 15451 | +#ifdef SQLITE_TEST | |
| 15452 | + if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){ | |
| 15453 | + /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite | |
| 15454 | + ** is using a regular VFS, it is called after the corresponding | |
| 15455 | + ** transaction has been committed. Injecting a fault at this point | |
| 15456 | + ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM | |
| 15457 | + ** but the transaction is committed anyway. | |
| 15458 | + ** | |
| 15459 | + ** The core must call OsFileControl() though, not OsFileControlHint(), | |
| 15460 | + ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably | |
| 15461 | + ** means the commit really has failed and an error should be returned | |
| 15462 | + ** to the user. */ | |
| 15463 | + DO_OS_MALLOC_TEST(id); | |
| 15464 | + } | |
| 15465 | +#endif | |
| 15449 | 15466 | return id->pMethods->xFileControl(id, op, pArg); |
| 15450 | 15467 | } |
| 15451 | 15468 | SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){ |
| 15452 | 15469 | (void)id->pMethods->xFileControl(id, op, pArg); |
| 15453 | 15470 | } |
| @@ -23098,11 +23115,11 @@ | ||
| 23098 | 23115 | /* 25 */ "String" OpHelp("r[P2]='P4' (len=P1)"), |
| 23099 | 23116 | /* 26 */ "Null" OpHelp("r[P2..P3]=NULL"), |
| 23100 | 23117 | /* 27 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), |
| 23101 | 23118 | /* 28 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), |
| 23102 | 23119 | /* 29 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), |
| 23103 | - /* 30 */ "Copy" OpHelp("r[P2@P3]=r[P1@P3]"), | |
| 23120 | + /* 30 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), | |
| 23104 | 23121 | /* 31 */ "SCopy" OpHelp("r[P2]=r[P1]"), |
| 23105 | 23122 | /* 32 */ "ResultRow" OpHelp("output=r[P1@P2]"), |
| 23106 | 23123 | /* 33 */ "CollSeq" OpHelp(""), |
| 23107 | 23124 | /* 34 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), |
| 23108 | 23125 | /* 35 */ "MustBeInt" OpHelp(""), |
| @@ -61223,11 +61240,12 @@ | ||
| 61223 | 61240 | */ |
| 61224 | 61241 | |
| 61225 | 61242 | /* |
| 61226 | 61243 | ** Create a new virtual database engine. |
| 61227 | 61244 | */ |
| 61228 | -SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){ | |
| 61245 | +SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){ | |
| 61246 | + sqlite3 *db = pParse->db; | |
| 61229 | 61247 | Vdbe *p; |
| 61230 | 61248 | p = sqlite3DbMallocZero(db, sizeof(Vdbe) ); |
| 61231 | 61249 | if( p==0 ) return 0; |
| 61232 | 61250 | p->db = db; |
| 61233 | 61251 | if( db->pVdbe ){ |
| @@ -61235,10 +61253,13 @@ | ||
| 61235 | 61253 | } |
| 61236 | 61254 | p->pNext = db->pVdbe; |
| 61237 | 61255 | p->pPrev = 0; |
| 61238 | 61256 | db->pVdbe = p; |
| 61239 | 61257 | p->magic = VDBE_MAGIC_INIT; |
| 61258 | +#if SQLITE_DEBUG | |
| 61259 | + p->pParse = pParse; | |
| 61260 | +#endif | |
| 61240 | 61261 | return p; |
| 61241 | 61262 | } |
| 61242 | 61263 | |
| 61243 | 61264 | /* |
| 61244 | 61265 | ** Remember the SQL string for a prepared statement. |
| @@ -61354,10 +61375,19 @@ | ||
| 61354 | 61375 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 61355 | 61376 | pOp->zComment = 0; |
| 61356 | 61377 | #endif |
| 61357 | 61378 | #ifdef SQLITE_DEBUG |
| 61358 | 61379 | if( p->db->flags & SQLITE_VdbeAddopTrace ){ |
| 61380 | + int jj, kk; | |
| 61381 | + Parse *pParse = p->pParse; | |
| 61382 | + for(jj=kk=0; jj<SQLITE_N_COLCACHE; jj++){ | |
| 61383 | + struct yColCache *x = pParse->aColCache + jj; | |
| 61384 | + if( x->iLevel>pParse->iCacheLevel || x->iReg==0 ) continue; | |
| 61385 | + printf(" r[%d]={%d:%d}", x->iReg, x->iTable, x->iColumn); | |
| 61386 | + kk++; | |
| 61387 | + } | |
| 61388 | + if( kk ) printf("\n"); | |
| 61359 | 61389 | sqlite3VdbePrintOp(0, i, &p->aOp[i]); |
| 61360 | 61390 | test_addop_breakpoint(); |
| 61361 | 61391 | } |
| 61362 | 61392 | #endif |
| 61363 | 61393 | #ifdef VDBE_PROFILE |
| @@ -62075,11 +62105,21 @@ | ||
| 62075 | 62105 | if( c=='4' ) return pOp->p4.i; |
| 62076 | 62106 | return pOp->p5; |
| 62077 | 62107 | } |
| 62078 | 62108 | |
| 62079 | 62109 | /* |
| 62080 | -** Compute a string for the "comment" field of a VDBE opcode listing | |
| 62110 | +** Compute a string for the "comment" field of a VDBE opcode listing. | |
| 62111 | +** | |
| 62112 | +** The Synopsis: field in comments in the vdbe.c source file gets converted | |
| 62113 | +** to an extra string that is appended to the sqlite3OpcodeName(). In the | |
| 62114 | +** absence of other comments, this synopsis becomes the comment on the opcode. | |
| 62115 | +** Some translation occurs: | |
| 62116 | +** | |
| 62117 | +** "PX" -> "r[X]" | |
| 62118 | +** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1 | |
| 62119 | +** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0 | |
| 62120 | +** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x | |
| 62081 | 62121 | */ |
| 62082 | 62122 | static int displayComment( |
| 62083 | 62123 | const Op *pOp, /* The opcode to be commented */ |
| 62084 | 62124 | const char *zP4, /* Previously obtained value for P4 */ |
| 62085 | 62125 | char *zTemp, /* Write result here */ |
| @@ -62109,11 +62149,17 @@ | ||
| 62109 | 62149 | sqlite3_snprintf(nTemp-jj, zTemp+jj, "%d", v1); |
| 62110 | 62150 | if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){ |
| 62111 | 62151 | ii += 3; |
| 62112 | 62152 | jj += sqlite3Strlen30(zTemp+jj); |
| 62113 | 62153 | v2 = translateP(zSynopsis[ii], pOp); |
| 62114 | - if( v2>1 ) sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1); | |
| 62154 | + if( strncmp(zSynopsis+ii+1,"+1",2)==0 ){ | |
| 62155 | + ii += 2; | |
| 62156 | + v2++; | |
| 62157 | + } | |
| 62158 | + if( v2>1 ){ | |
| 62159 | + sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1); | |
| 62160 | + } | |
| 62115 | 62161 | }else if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){ |
| 62116 | 62162 | ii += 4; |
| 62117 | 62163 | } |
| 62118 | 62164 | } |
| 62119 | 62165 | jj += sqlite3Strlen30(zTemp+jj); |
| @@ -62341,10 +62387,13 @@ | ||
| 62341 | 62387 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 62342 | 62388 | displayComment(pOp, zP4, zCom, sizeof(zCom)); |
| 62343 | 62389 | #else |
| 62344 | 62390 | zCom[0] = 0 |
| 62345 | 62391 | #endif |
| 62392 | + /* NB: The sqlite3OpcodeName() function is implemented by code created | |
| 62393 | + ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the | |
| 62394 | + ** information from the vdbe.c source text */ | |
| 62346 | 62395 | fprintf(pOut, zFormat1, pc, |
| 62347 | 62396 | sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5, |
| 62348 | 62397 | zCom |
| 62349 | 62398 | ); |
| 62350 | 62399 | fflush(pOut); |
| @@ -67401,11 +67450,11 @@ | ||
| 67401 | 67450 | }while( n-- ); |
| 67402 | 67451 | break; |
| 67403 | 67452 | } |
| 67404 | 67453 | |
| 67405 | 67454 | /* Opcode: Copy P1 P2 P3 * * |
| 67406 | -** Synopsis: r[P2@P3]=r[P1@P3] | |
| 67455 | +** Synopsis: r[P2@P3+1]=r[P1@P3+1] | |
| 67407 | 67456 | ** |
| 67408 | 67457 | ** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. |
| 67409 | 67458 | ** |
| 67410 | 67459 | ** This instruction makes a deep copy of the value. A duplicate |
| 67411 | 67460 | ** is made of any string or blob constant. See also OP_SCopy. |
| @@ -68744,11 +68793,11 @@ | ||
| 68744 | 68793 | /* This is the common case where the desired content fits on the original |
| 68745 | 68794 | ** page - where the content is not on an overflow page */ |
| 68746 | 68795 | VdbeMemRelease(pDest); |
| 68747 | 68796 | sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], aType[p2], pDest); |
| 68748 | 68797 | }else{ |
| 68749 | - /* This branch happens only when content is on overflow pages */ | |
| 68798 | + /* This branch happens only when content is on overflow pages */ | |
| 68750 | 68799 | t = aType[p2]; |
| 68751 | 68800 | if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 |
| 68752 | 68801 | && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) |
| 68753 | 68802 | || (len = sqlite3VdbeSerialTypeLen(t))==0 |
| 68754 | 68803 | ){ |
| @@ -72862,11 +72911,11 @@ | ||
| 72862 | 72911 | sqlite3BtreeLeaveAll(db); |
| 72863 | 72912 | goto blob_open_out; |
| 72864 | 72913 | } |
| 72865 | 72914 | } |
| 72866 | 72915 | |
| 72867 | - pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db); | |
| 72916 | + pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse); | |
| 72868 | 72917 | assert( pBlob->pStmt || db->mallocFailed ); |
| 72869 | 72918 | if( pBlob->pStmt ){ |
| 72870 | 72919 | Vdbe *v = (Vdbe *)pBlob->pStmt; |
| 72871 | 72920 | int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 72872 | 72921 | |
| @@ -78416,10 +78465,15 @@ | ||
| 78416 | 78465 | ** added to the column cache after this call are removed when the |
| 78417 | 78466 | ** corresponding pop occurs. |
| 78418 | 78467 | */ |
| 78419 | 78468 | SQLITE_PRIVATE void sqlite3ExprCachePush(Parse *pParse){ |
| 78420 | 78469 | pParse->iCacheLevel++; |
| 78470 | +#ifdef SQLITE_DEBUG | |
| 78471 | + if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ | |
| 78472 | + printf("PUSH to %d\n", pParse->iCacheLevel); | |
| 78473 | + } | |
| 78474 | +#endif | |
| 78421 | 78475 | } |
| 78422 | 78476 | |
| 78423 | 78477 | /* |
| 78424 | 78478 | ** Remove from the column cache any entries that were added since the |
| 78425 | 78479 | ** the previous N Push operations. In other words, restore the cache |
| @@ -78429,10 +78483,15 @@ | ||
| 78429 | 78483 | int i; |
| 78430 | 78484 | struct yColCache *p; |
| 78431 | 78485 | assert( N>0 ); |
| 78432 | 78486 | assert( pParse->iCacheLevel>=N ); |
| 78433 | 78487 | pParse->iCacheLevel -= N; |
| 78488 | +#ifdef SQLITE_DEBUG | |
| 78489 | + if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ | |
| 78490 | + printf("POP to %d\n", pParse->iCacheLevel); | |
| 78491 | + } | |
| 78492 | +#endif | |
| 78434 | 78493 | for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ |
| 78435 | 78494 | if( p->iReg && p->iLevel>pParse->iCacheLevel ){ |
| 78436 | 78495 | cacheEntryClear(pParse, p); |
| 78437 | 78496 | p->iReg = 0; |
| 78438 | 78497 | } |
| @@ -78523,10 +78582,15 @@ | ||
| 78523 | 78582 | */ |
| 78524 | 78583 | SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse *pParse){ |
| 78525 | 78584 | int i; |
| 78526 | 78585 | struct yColCache *p; |
| 78527 | 78586 | |
| 78587 | +#if SQLITE_DEBUG | |
| 78588 | + if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ | |
| 78589 | + printf("CLEAR\n"); | |
| 78590 | + } | |
| 78591 | +#endif | |
| 78528 | 78592 | for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ |
| 78529 | 78593 | if( p->iReg ){ |
| 78530 | 78594 | cacheEntryClear(pParse, p); |
| 78531 | 78595 | p->iReg = 0; |
| 78532 | 78596 | } |
| @@ -79659,11 +79723,21 @@ | ||
| 79659 | 79723 | if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){ |
| 79660 | 79724 | sqlite3ExprCodeAtInit(pParse, pExpr, target+i, 0); |
| 79661 | 79725 | }else{ |
| 79662 | 79726 | int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); |
| 79663 | 79727 | if( inReg!=target+i ){ |
| 79664 | - sqlite3VdbeAddOp2(pParse->pVdbe, copyOp, inReg, target+i); | |
| 79728 | + VdbeOp *pOp; | |
| 79729 | + Vdbe *v = pParse->pVdbe; | |
| 79730 | + if( copyOp==OP_Copy | |
| 79731 | + && (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy | |
| 79732 | + && pOp->p1+pOp->p3+1==inReg | |
| 79733 | + && pOp->p2+pOp->p3+1==target+i | |
| 79734 | + ){ | |
| 79735 | + pOp->p3++; | |
| 79736 | + }else{ | |
| 79737 | + sqlite3VdbeAddOp2(v, copyOp, inReg, target+i); | |
| 79738 | + } | |
| 79665 | 79739 | } |
| 79666 | 79740 | } |
| 79667 | 79741 | } |
| 79668 | 79742 | return n; |
| 79669 | 79743 | } |
| @@ -83083,14 +83157,10 @@ | ||
| 83083 | 83157 | { |
| 83084 | 83158 | int rc = SQLITE_OK; |
| 83085 | 83159 | if( pExpr ){ |
| 83086 | 83160 | if( pExpr->op!=TK_ID ){ |
| 83087 | 83161 | rc = sqlite3ResolveExprNames(pName, pExpr); |
| 83088 | - if( rc==SQLITE_OK && !sqlite3ExprIsConstant(pExpr) ){ | |
| 83089 | - sqlite3ErrorMsg(pName->pParse, "invalid name: \"%s\"", pExpr->u.zToken); | |
| 83090 | - return SQLITE_ERROR; | |
| 83091 | - } | |
| 83092 | 83162 | }else{ |
| 83093 | 83163 | pExpr->op = TK_STRING; |
| 83094 | 83164 | } |
| 83095 | 83165 | } |
| 83096 | 83166 | return rc; |
| @@ -89324,11 +89394,10 @@ | ||
| 89324 | 89394 | Vdbe *v = pParse->pVdbe; |
| 89325 | 89395 | int j; |
| 89326 | 89396 | Table *pTab = pIdx->pTable; |
| 89327 | 89397 | int regBase; |
| 89328 | 89398 | int nCol; |
| 89329 | - Index *pPk; | |
| 89330 | 89399 | |
| 89331 | 89400 | if( piPartIdxLabel ){ |
| 89332 | 89401 | if( pIdx->pPartIdxWhere ){ |
| 89333 | 89402 | *piPartIdxLabel = sqlite3VdbeMakeLabel(v); |
| 89334 | 89403 | pParse->iPartIdxTab = iDataCur; |
| @@ -89338,20 +89407,13 @@ | ||
| 89338 | 89407 | *piPartIdxLabel = 0; |
| 89339 | 89408 | } |
| 89340 | 89409 | } |
| 89341 | 89410 | nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn; |
| 89342 | 89411 | regBase = sqlite3GetTempRange(pParse, nCol); |
| 89343 | - pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); | |
| 89344 | 89412 | for(j=0; j<nCol; j++){ |
| 89345 | - i16 idx = pIdx->aiColumn[j]; | |
| 89346 | - if( pPk ) idx = sqlite3ColumnOfIndex(pPk, idx); | |
| 89347 | - if( idx<0 || idx==pTab->iPKey ){ | |
| 89348 | - sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regBase+j); | |
| 89349 | - }else{ | |
| 89350 | - sqlite3VdbeAddOp3(v, OP_Column, iDataCur, idx, regBase+j); | |
| 89351 | - sqlite3ColumnDefault(v, pTab, pIdx->aiColumn[j], -1); | |
| 89352 | - } | |
| 89413 | + sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j], | |
| 89414 | + regBase+j); | |
| 89353 | 89415 | } |
| 89354 | 89416 | if( regOut ){ |
| 89355 | 89417 | const char *zAff; |
| 89356 | 89418 | if( pTab->pSelect |
| 89357 | 89419 | || OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt) |
| @@ -93982,51 +94044,53 @@ | ||
| 93982 | 94044 | sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk, |
| 93983 | 94045 | regIdx, pIdx->nKeyCol); |
| 93984 | 94046 | |
| 93985 | 94047 | /* Generate code to handle collisions */ |
| 93986 | 94048 | regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField); |
| 93987 | - if( HasRowid(pTab) ){ | |
| 93988 | - sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); | |
| 93989 | - /* Conflict only if the rowid of the existing index entry | |
| 93990 | - ** is different from old-rowid */ | |
| 93991 | - if( isUpdate ){ | |
| 93992 | - sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData); | |
| 93993 | - } | |
| 93994 | - }else{ | |
| 93995 | - int x; | |
| 93996 | - /* Extract the PRIMARY KEY from the end of the index entry and | |
| 93997 | - ** store it in registers regR..regR+nPk-1 */ | |
| 93998 | - if( (isUpdate || onError==OE_Replace) && pIdx!=pPk ){ | |
| 93999 | - for(i=0; i<pPk->nKeyCol; i++){ | |
| 94000 | - x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]); | |
| 94001 | - sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); | |
| 94002 | - VdbeComment((v, "%s.%s", pTab->zName, | |
| 94003 | - pTab->aCol[pPk->aiColumn[i]].zName)); | |
| 94004 | - } | |
| 94005 | - } | |
| 94006 | - if( isUpdate ){ | |
| 94007 | - /* If currently processing the PRIMARY KEY of a WITHOUT ROWID | |
| 94008 | - ** table, only conflict if the new PRIMARY KEY values are actually | |
| 94009 | - ** different from the old. | |
| 94010 | - ** | |
| 94011 | - ** For a UNIQUE index, only conflict if the PRIMARY KEY values | |
| 94012 | - ** of the matched index row are different from the original PRIMARY | |
| 94013 | - ** KEY values of this row before the update. */ | |
| 94014 | - int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; | |
| 94015 | - int op = OP_Ne; | |
| 94016 | - int regCmp = (pIdx->autoIndex==2 ? regIdx : regR); | |
| 94017 | - | |
| 94018 | - for(i=0; i<pPk->nKeyCol; i++){ | |
| 94019 | - char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]); | |
| 94020 | - x = pPk->aiColumn[i]; | |
| 94021 | - if( i==(pPk->nKeyCol-1) ){ | |
| 94022 | - addrJump = addrUniqueOk; | |
| 94023 | - op = OP_Eq; | |
| 94024 | - } | |
| 94025 | - sqlite3VdbeAddOp4(v, op, | |
| 94026 | - regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ | |
| 94027 | - ); | |
| 94049 | + if( isUpdate || onError==OE_Replace ){ | |
| 94050 | + if( HasRowid(pTab) ){ | |
| 94051 | + sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); | |
| 94052 | + /* Conflict only if the rowid of the existing index entry | |
| 94053 | + ** is different from old-rowid */ | |
| 94054 | + if( isUpdate ){ | |
| 94055 | + sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData); | |
| 94056 | + } | |
| 94057 | + }else{ | |
| 94058 | + int x; | |
| 94059 | + /* Extract the PRIMARY KEY from the end of the index entry and | |
| 94060 | + ** store it in registers regR..regR+nPk-1 */ | |
| 94061 | + if( pIdx!=pPk ){ | |
| 94062 | + for(i=0; i<pPk->nKeyCol; i++){ | |
| 94063 | + x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]); | |
| 94064 | + sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); | |
| 94065 | + VdbeComment((v, "%s.%s", pTab->zName, | |
| 94066 | + pTab->aCol[pPk->aiColumn[i]].zName)); | |
| 94067 | + } | |
| 94068 | + } | |
| 94069 | + if( isUpdate ){ | |
| 94070 | + /* If currently processing the PRIMARY KEY of a WITHOUT ROWID | |
| 94071 | + ** table, only conflict if the new PRIMARY KEY values are actually | |
| 94072 | + ** different from the old. | |
| 94073 | + ** | |
| 94074 | + ** For a UNIQUE index, only conflict if the PRIMARY KEY values | |
| 94075 | + ** of the matched index row are different from the original PRIMARY | |
| 94076 | + ** KEY values of this row before the update. */ | |
| 94077 | + int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; | |
| 94078 | + int op = OP_Ne; | |
| 94079 | + int regCmp = (pIdx->autoIndex==2 ? regIdx : regR); | |
| 94080 | + | |
| 94081 | + for(i=0; i<pPk->nKeyCol; i++){ | |
| 94082 | + char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]); | |
| 94083 | + x = pPk->aiColumn[i]; | |
| 94084 | + if( i==(pPk->nKeyCol-1) ){ | |
| 94085 | + addrJump = addrUniqueOk; | |
| 94086 | + op = OP_Eq; | |
| 94087 | + } | |
| 94088 | + sqlite3VdbeAddOp4(v, op, | |
| 94089 | + regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ | |
| 94090 | + ); | |
| 94091 | + } | |
| 94028 | 94092 | } |
| 94029 | 94093 | } |
| 94030 | 94094 | } |
| 94031 | 94095 | |
| 94032 | 94096 | /* Generate code that executes if the new index entry is not unique */ |
| @@ -99722,11 +99786,10 @@ | ||
| 99722 | 99786 | } |
| 99723 | 99787 | }else if( eDest!=SRT_Exists ){ |
| 99724 | 99788 | /* If the destination is an EXISTS(...) expression, the actual |
| 99725 | 99789 | ** values returned by the SELECT are not required. |
| 99726 | 99790 | */ |
| 99727 | - sqlite3ExprCacheClear(pParse); | |
| 99728 | 99791 | sqlite3ExprCodeExprList(pParse, pEList, regResult, |
| 99729 | 99792 | (eDest==SRT_Output)?SQLITE_ECEL_DUP:0); |
| 99730 | 99793 | } |
| 99731 | 99794 | nColumn = nResultCol; |
| 99732 | 99795 | |
| @@ -100689,11 +100752,11 @@ | ||
| 100689 | 100752 | ** If an error occurs, return NULL and leave a message in pParse. |
| 100690 | 100753 | */ |
| 100691 | 100754 | SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){ |
| 100692 | 100755 | Vdbe *v = pParse->pVdbe; |
| 100693 | 100756 | if( v==0 ){ |
| 100694 | - v = pParse->pVdbe = sqlite3VdbeCreate(pParse->db); | |
| 100757 | + v = pParse->pVdbe = sqlite3VdbeCreate(pParse); | |
| 100695 | 100758 | #ifndef SQLITE_OMIT_TRACE |
| 100696 | 100759 | if( v ){ |
| 100697 | 100760 | sqlite3VdbeAddOp0(v, OP_Trace); |
| 100698 | 100761 | } |
| 100699 | 100762 | #endif |
| @@ -102947,18 +103010,27 @@ | ||
| 102947 | 103010 | */ |
| 102948 | 103011 | static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ |
| 102949 | 103012 | Vdbe *v = pParse->pVdbe; |
| 102950 | 103013 | int i; |
| 102951 | 103014 | struct AggInfo_func *pFunc; |
| 102952 | - if( pAggInfo->nFunc+pAggInfo->nColumn==0 ){ | |
| 102953 | - return; | |
| 102954 | - } | |
| 103015 | + int nReg = pAggInfo->nFunc + pAggInfo->nColumn; | |
| 103016 | + if( nReg==0 ) return; | |
| 103017 | +#ifdef SQLITE_DEBUG | |
| 103018 | + /* Verify that all AggInfo registers are within the range specified by | |
| 103019 | + ** AggInfo.mnReg..AggInfo.mxReg */ | |
| 103020 | + assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 ); | |
| 102955 | 103021 | for(i=0; i<pAggInfo->nColumn; i++){ |
| 102956 | - sqlite3VdbeAddOp2(v, OP_Null, 0, pAggInfo->aCol[i].iMem); | |
| 103022 | + assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg | |
| 103023 | + && pAggInfo->aCol[i].iMem<=pAggInfo->mxReg ); | |
| 102957 | 103024 | } |
| 103025 | + for(i=0; i<pAggInfo->nFunc; i++){ | |
| 103026 | + assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg | |
| 103027 | + && pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg ); | |
| 103028 | + } | |
| 103029 | +#endif | |
| 103030 | + sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg); | |
| 102958 | 103031 | for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){ |
| 102959 | - sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem); | |
| 102960 | 103032 | if( pFunc->iDistinct>=0 ){ |
| 102961 | 103033 | Expr *pE = pFunc->pExpr; |
| 102962 | 103034 | assert( !ExprHasProperty(pE, EP_xIsSelect) ); |
| 102963 | 103035 | if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ |
| 102964 | 103036 | sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " |
| @@ -103000,11 +103072,10 @@ | ||
| 103000 | 103072 | int addrHitTest = 0; |
| 103001 | 103073 | struct AggInfo_func *pF; |
| 103002 | 103074 | struct AggInfo_col *pC; |
| 103003 | 103075 | |
| 103004 | 103076 | pAggInfo->directMode = 1; |
| 103005 | - sqlite3ExprCacheClear(pParse); | |
| 103006 | 103077 | for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){ |
| 103007 | 103078 | int nArg; |
| 103008 | 103079 | int addrNext = 0; |
| 103009 | 103080 | int regAgg; |
| 103010 | 103081 | ExprList *pList = pF->pExpr->x.pList; |
| @@ -103533,10 +103604,11 @@ | ||
| 103533 | 103604 | */ |
| 103534 | 103605 | memset(&sNC, 0, sizeof(sNC)); |
| 103535 | 103606 | sNC.pParse = pParse; |
| 103536 | 103607 | sNC.pSrcList = pTabList; |
| 103537 | 103608 | sNC.pAggInfo = &sAggInfo; |
| 103609 | + sAggInfo.mnReg = pParse->nMem+1; | |
| 103538 | 103610 | sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0; |
| 103539 | 103611 | sAggInfo.pGroupBy = pGroupBy; |
| 103540 | 103612 | sqlite3ExprAnalyzeAggList(&sNC, pEList); |
| 103541 | 103613 | sqlite3ExprAnalyzeAggList(&sNC, pOrderBy); |
| 103542 | 103614 | if( pHaving ){ |
| @@ -103547,10 +103619,11 @@ | ||
| 103547 | 103619 | assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) ); |
| 103548 | 103620 | sNC.ncFlags |= NC_InAggFunc; |
| 103549 | 103621 | sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList); |
| 103550 | 103622 | sNC.ncFlags &= ~NC_InAggFunc; |
| 103551 | 103623 | } |
| 103624 | + sAggInfo.mxReg = pParse->nMem; | |
| 103552 | 103625 | if( db->mallocFailed ) goto select_end; |
| 103553 | 103626 | |
| 103554 | 103627 | /* Processing for aggregates with GROUP BY is very different and |
| 103555 | 103628 | ** much more complex than aggregates without a GROUP BY. |
| 103556 | 103629 | */ |
| @@ -105451,11 +105524,11 @@ | ||
| 105451 | 105524 | pCol->affinity, &pValue); |
| 105452 | 105525 | if( pValue ){ |
| 105453 | 105526 | sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM); |
| 105454 | 105527 | } |
| 105455 | 105528 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 105456 | - if( iReg>=0 && pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ | |
| 105529 | + if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ | |
| 105457 | 105530 | sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); |
| 105458 | 105531 | } |
| 105459 | 105532 | #endif |
| 105460 | 105533 | } |
| 105461 | 105534 | } |
| @@ -105875,14 +105948,14 @@ | ||
| 105875 | 105948 | ** be used eliminates some redundant opcodes. |
| 105876 | 105949 | */ |
| 105877 | 105950 | newmask = sqlite3TriggerColmask( |
| 105878 | 105951 | pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError |
| 105879 | 105952 | ); |
| 105880 | - sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1); | |
| 105953 | + /*sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);*/ | |
| 105881 | 105954 | for(i=0; i<pTab->nCol; i++){ |
| 105882 | 105955 | if( i==pTab->iPKey ){ |
| 105883 | - /*sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);*/ | |
| 105956 | + sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); | |
| 105884 | 105957 | }else{ |
| 105885 | 105958 | j = aXRef[i]; |
| 105886 | 105959 | if( j>=0 ){ |
| 105887 | 105960 | sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); |
| 105888 | 105961 | }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<<i)) ){ |
| @@ -105892,10 +105965,12 @@ | ||
| 105892 | 105965 | ** a new.* reference in a trigger program. |
| 105893 | 105966 | */ |
| 105894 | 105967 | testcase( i==31 ); |
| 105895 | 105968 | testcase( i==32 ); |
| 105896 | 105969 | sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i); |
| 105970 | + }else{ | |
| 105971 | + sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); | |
| 105897 | 105972 | } |
| 105898 | 105973 | } |
| 105899 | 105974 | } |
| 105900 | 105975 | |
| 105901 | 105976 | /* Fire any BEFORE UPDATE triggers. This happens before constraints are |
| @@ -112004,10 +112079,11 @@ | ||
| 112004 | 112079 | */ |
| 112005 | 112080 | if( pTerm==0 |
| 112006 | 112081 | && saved_nEq==saved_nSkip |
| 112007 | 112082 | && saved_nEq+1<pProbe->nKeyCol |
| 112008 | 112083 | && pProbe->aiRowEst[saved_nEq+1]>=18 /* TUNING: Minimum for skip-scan */ |
| 112084 | + && (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK | |
| 112009 | 112085 | ){ |
| 112010 | 112086 | LogEst nIter; |
| 112011 | 112087 | pNew->u.btree.nEq++; |
| 112012 | 112088 | pNew->u.btree.nSkip++; |
| 112013 | 112089 | pNew->aLTerm[pNew->nLTerm++] = 0; |
| @@ -119722,12 +119798,11 @@ | ||
| 119722 | 119798 | |
| 119723 | 119799 | /* |
| 119724 | 119800 | ** Return a static string containing the name corresponding to the error code |
| 119725 | 119801 | ** specified in the argument. |
| 119726 | 119802 | */ |
| 119727 | -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \ | |
| 119728 | - defined(SQLITE_DEBUG_OS_TRACE) | |
| 119803 | +#if defined(SQLITE_TEST) | |
| 119729 | 119804 | SQLITE_PRIVATE const char *sqlite3ErrName(int rc){ |
| 119730 | 119805 | const char *zName = 0; |
| 119731 | 119806 | int i, origRc = rc; |
| 119732 | 119807 | for(i=0; i<2 && zName==0; i++, rc &= 0xff){ |
| 119733 | 119808 | switch( rc ){ |
| @@ -121300,10 +121375,12 @@ | ||
| 121300 | 121375 | #ifdef SQLITE_DEFAULT_LOCKING_MODE |
| 121301 | 121376 | db->dfltLockMode = SQLITE_DEFAULT_LOCKING_MODE; |
| 121302 | 121377 | sqlite3PagerLockingMode(sqlite3BtreePager(db->aDb[0].pBt), |
| 121303 | 121378 | SQLITE_DEFAULT_LOCKING_MODE); |
| 121304 | 121379 | #endif |
| 121380 | + | |
| 121381 | + if( rc ) sqlite3Error(db, rc, 0); | |
| 121305 | 121382 | |
| 121306 | 121383 | /* Enable the lookaside-malloc subsystem */ |
| 121307 | 121384 | setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside, |
| 121308 | 121385 | sqlite3GlobalConfig.nLookaside); |
| 121309 | 121386 | |
| @@ -131632,61 +131709,74 @@ | ||
| 131632 | 131709 | } |
| 131633 | 131710 | |
| 131634 | 131711 | /* Step 2 */ |
| 131635 | 131712 | switch( z[1] ){ |
| 131636 | 131713 | case 'a': |
| 131637 | - stem(&z, "lanoita", "ate", m_gt_0) || | |
| 131638 | - stem(&z, "lanoit", "tion", m_gt_0); | |
| 131714 | + if( !stem(&z, "lanoita", "ate", m_gt_0) ){ | |
| 131715 | + stem(&z, "lanoit", "tion", m_gt_0); | |
| 131716 | + } | |
| 131639 | 131717 | break; |
| 131640 | 131718 | case 'c': |
| 131641 | - stem(&z, "icne", "ence", m_gt_0) || | |
| 131642 | - stem(&z, "icna", "ance", m_gt_0); | |
| 131719 | + if( !stem(&z, "icne", "ence", m_gt_0) ){ | |
| 131720 | + stem(&z, "icna", "ance", m_gt_0); | |
| 131721 | + } | |
| 131643 | 131722 | break; |
| 131644 | 131723 | case 'e': |
| 131645 | 131724 | stem(&z, "rezi", "ize", m_gt_0); |
| 131646 | 131725 | break; |
| 131647 | 131726 | case 'g': |
| 131648 | 131727 | stem(&z, "igol", "log", m_gt_0); |
| 131649 | 131728 | break; |
| 131650 | 131729 | case 'l': |
| 131651 | - stem(&z, "ilb", "ble", m_gt_0) || | |
| 131652 | - stem(&z, "illa", "al", m_gt_0) || | |
| 131653 | - stem(&z, "iltne", "ent", m_gt_0) || | |
| 131654 | - stem(&z, "ile", "e", m_gt_0) || | |
| 131655 | - stem(&z, "ilsuo", "ous", m_gt_0); | |
| 131730 | + if( !stem(&z, "ilb", "ble", m_gt_0) | |
| 131731 | + && !stem(&z, "illa", "al", m_gt_0) | |
| 131732 | + && !stem(&z, "iltne", "ent", m_gt_0) | |
| 131733 | + && !stem(&z, "ile", "e", m_gt_0) | |
| 131734 | + ){ | |
| 131735 | + stem(&z, "ilsuo", "ous", m_gt_0); | |
| 131736 | + } | |
| 131656 | 131737 | break; |
| 131657 | 131738 | case 'o': |
| 131658 | - stem(&z, "noitazi", "ize", m_gt_0) || | |
| 131659 | - stem(&z, "noita", "ate", m_gt_0) || | |
| 131660 | - stem(&z, "rota", "ate", m_gt_0); | |
| 131739 | + if( !stem(&z, "noitazi", "ize", m_gt_0) | |
| 131740 | + && !stem(&z, "noita", "ate", m_gt_0) | |
| 131741 | + ){ | |
| 131742 | + stem(&z, "rota", "ate", m_gt_0); | |
| 131743 | + } | |
| 131661 | 131744 | break; |
| 131662 | 131745 | case 's': |
| 131663 | - stem(&z, "msila", "al", m_gt_0) || | |
| 131664 | - stem(&z, "ssenevi", "ive", m_gt_0) || | |
| 131665 | - stem(&z, "ssenluf", "ful", m_gt_0) || | |
| 131666 | - stem(&z, "ssensuo", "ous", m_gt_0); | |
| 131746 | + if( !stem(&z, "msila", "al", m_gt_0) | |
| 131747 | + && !stem(&z, "ssenevi", "ive", m_gt_0) | |
| 131748 | + && !stem(&z, "ssenluf", "ful", m_gt_0) | |
| 131749 | + ){ | |
| 131750 | + stem(&z, "ssensuo", "ous", m_gt_0); | |
| 131751 | + } | |
| 131667 | 131752 | break; |
| 131668 | 131753 | case 't': |
| 131669 | - stem(&z, "itila", "al", m_gt_0) || | |
| 131670 | - stem(&z, "itivi", "ive", m_gt_0) || | |
| 131671 | - stem(&z, "itilib", "ble", m_gt_0); | |
| 131754 | + if( !stem(&z, "itila", "al", m_gt_0) | |
| 131755 | + && !stem(&z, "itivi", "ive", m_gt_0) | |
| 131756 | + ){ | |
| 131757 | + stem(&z, "itilib", "ble", m_gt_0); | |
| 131758 | + } | |
| 131672 | 131759 | break; |
| 131673 | 131760 | } |
| 131674 | 131761 | |
| 131675 | 131762 | /* Step 3 */ |
| 131676 | 131763 | switch( z[0] ){ |
| 131677 | 131764 | case 'e': |
| 131678 | - stem(&z, "etaci", "ic", m_gt_0) || | |
| 131679 | - stem(&z, "evita", "", m_gt_0) || | |
| 131680 | - stem(&z, "ezila", "al", m_gt_0); | |
| 131765 | + if( !stem(&z, "etaci", "ic", m_gt_0) | |
| 131766 | + && !stem(&z, "evita", "", m_gt_0) | |
| 131767 | + ){ | |
| 131768 | + stem(&z, "ezila", "al", m_gt_0); | |
| 131769 | + } | |
| 131681 | 131770 | break; |
| 131682 | 131771 | case 'i': |
| 131683 | 131772 | stem(&z, "itici", "ic", m_gt_0); |
| 131684 | 131773 | break; |
| 131685 | 131774 | case 'l': |
| 131686 | - stem(&z, "laci", "ic", m_gt_0) || | |
| 131687 | - stem(&z, "luf", "", m_gt_0); | |
| 131775 | + if( !stem(&z, "laci", "ic", m_gt_0) ){ | |
| 131776 | + stem(&z, "luf", "", m_gt_0); | |
| 131777 | + } | |
| 131688 | 131778 | break; |
| 131689 | 131779 | case 's': |
| 131690 | 131780 | stem(&z, "ssen", "", m_gt_0); |
| 131691 | 131781 | break; |
| 131692 | 131782 | } |
| @@ -131723,13 +131813,15 @@ | ||
| 131723 | 131813 | if( z[2]=='a' ){ |
| 131724 | 131814 | if( m_gt_1(z+3) ){ |
| 131725 | 131815 | z += 3; |
| 131726 | 131816 | } |
| 131727 | 131817 | }else if( z[2]=='e' ){ |
| 131728 | - stem(&z, "tneme", "", m_gt_1) || | |
| 131729 | - stem(&z, "tnem", "", m_gt_1) || | |
| 131730 | - stem(&z, "tne", "", m_gt_1); | |
| 131818 | + if( !stem(&z, "tneme", "", m_gt_1) | |
| 131819 | + && !stem(&z, "tnem", "", m_gt_1) | |
| 131820 | + ){ | |
| 131821 | + stem(&z, "tne", "", m_gt_1); | |
| 131822 | + } | |
| 131731 | 131823 | } |
| 131732 | 131824 | } |
| 131733 | 131825 | break; |
| 131734 | 131826 | case 'o': |
| 131735 | 131827 | if( z[0]=='u' ){ |
| @@ -131744,12 +131836,13 @@ | ||
| 131744 | 131836 | if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){ |
| 131745 | 131837 | z += 3; |
| 131746 | 131838 | } |
| 131747 | 131839 | break; |
| 131748 | 131840 | case 't': |
| 131749 | - stem(&z, "eta", "", m_gt_1) || | |
| 131750 | - stem(&z, "iti", "", m_gt_1); | |
| 131841 | + if( !stem(&z, "eta", "", m_gt_1) ){ | |
| 131842 | + stem(&z, "iti", "", m_gt_1); | |
| 131843 | + } | |
| 131751 | 131844 | break; |
| 131752 | 131845 | case 'u': |
| 131753 | 131846 | if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){ |
| 131754 | 131847 | z += 3; |
| 131755 | 131848 | } |
| 131756 | 131849 |
| --- 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-17 16:32:56 93121d3097a43997af3c0de65bd9bd7663845fa2" |
| 141 | |
| 142 | /* |
| 143 | ** CAPI3REF: Run-Time Library Version Numbers |
| 144 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 145 | ** |
| @@ -9098,11 +9098,11 @@ | |
| 9098 | #define OP_String 25 /* synopsis: r[P2]='P4' (len=P1) */ |
| 9099 | #define OP_Null 26 /* synopsis: r[P2..P3]=NULL */ |
| 9100 | #define OP_Blob 27 /* synopsis: r[P2]=P4 (len=P1) */ |
| 9101 | #define OP_Variable 28 /* synopsis: r[P2]=parameter(P1,P4) */ |
| 9102 | #define OP_Move 29 /* synopsis: r[P2@P3]=r[P1@P3] */ |
| 9103 | #define OP_Copy 30 /* synopsis: r[P2@P3]=r[P1@P3] */ |
| 9104 | #define OP_SCopy 31 /* synopsis: r[P2]=r[P1] */ |
| 9105 | #define OP_ResultRow 32 /* synopsis: output=r[P1@P2] */ |
| 9106 | #define OP_CollSeq 33 |
| 9107 | #define OP_AddImm 34 /* synopsis: r[P1]=r[P1]+P2 */ |
| 9108 | #define OP_MustBeInt 35 |
| @@ -9263,11 +9263,11 @@ | |
| 9263 | |
| 9264 | /* |
| 9265 | ** Prototypes for the VDBE interface. See comments on the implementation |
| 9266 | ** for a description of what each of these routines does. |
| 9267 | */ |
| 9268 | SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3*); |
| 9269 | SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); |
| 9270 | SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); |
| 9271 | SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); |
| 9272 | SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); |
| 9273 | SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); |
| @@ -11003,10 +11003,11 @@ | |
| 11003 | u8 useSortingIdx; /* In direct mode, reference the sorting index rather |
| 11004 | ** than the source table */ |
| 11005 | int sortingIdx; /* Cursor number of the sorting index */ |
| 11006 | int sortingIdxPTab; /* Cursor number of pseudo-table */ |
| 11007 | int nSortingColumn; /* Number of columns in the sorting index */ |
| 11008 | ExprList *pGroupBy; /* The group by clause */ |
| 11009 | struct AggInfo_col { /* For each column used in source tables */ |
| 11010 | Table *pTab; /* Source table */ |
| 11011 | int iTable; /* Cursor number of the source table */ |
| 11012 | int iColumn; /* Column number within the source table */ |
| @@ -12452,12 +12453,11 @@ | |
| 12452 | SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...); |
| 12453 | SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); |
| 12454 | SQLITE_PRIVATE u8 sqlite3HexToInt(int h); |
| 12455 | SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); |
| 12456 | |
| 12457 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \ |
| 12458 | defined(SQLITE_DEBUG_OS_TRACE) |
| 12459 | SQLITE_PRIVATE const char *sqlite3ErrName(int); |
| 12460 | #endif |
| 12461 | |
| 12462 | SQLITE_PRIVATE const char *sqlite3ErrStr(int); |
| 12463 | SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); |
| @@ -13776,10 +13776,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 | int nMem; /* Number of memory locations currently allocated */ |
| 13782 | int nOp; /* Number of instructions in the program */ |
| 13783 | int nOpAlloc; /* Number of slots allocated for aOp[] */ |
| 13784 | int nLabel; /* Number of labels used */ |
| 13785 | int *aLabel; /* Space to hold the labels */ |
| @@ -15443,11 +15446,25 @@ | |
| 15443 | ** really care if the VFS receives and understands the information since it |
| 15444 | ** is only a hint and can be safely ignored. The sqlite3OsFileControlHint() |
| 15445 | ** routine has no return value since the return value would be meaningless. |
| 15446 | */ |
| 15447 | SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ |
| 15448 | DO_OS_MALLOC_TEST(id); |
| 15449 | return id->pMethods->xFileControl(id, op, pArg); |
| 15450 | } |
| 15451 | SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){ |
| 15452 | (void)id->pMethods->xFileControl(id, op, pArg); |
| 15453 | } |
| @@ -23098,11 +23115,11 @@ | |
| 23098 | /* 25 */ "String" OpHelp("r[P2]='P4' (len=P1)"), |
| 23099 | /* 26 */ "Null" OpHelp("r[P2..P3]=NULL"), |
| 23100 | /* 27 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), |
| 23101 | /* 28 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), |
| 23102 | /* 29 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), |
| 23103 | /* 30 */ "Copy" OpHelp("r[P2@P3]=r[P1@P3]"), |
| 23104 | /* 31 */ "SCopy" OpHelp("r[P2]=r[P1]"), |
| 23105 | /* 32 */ "ResultRow" OpHelp("output=r[P1@P2]"), |
| 23106 | /* 33 */ "CollSeq" OpHelp(""), |
| 23107 | /* 34 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), |
| 23108 | /* 35 */ "MustBeInt" OpHelp(""), |
| @@ -61223,11 +61240,12 @@ | |
| 61223 | */ |
| 61224 | |
| 61225 | /* |
| 61226 | ** Create a new virtual database engine. |
| 61227 | */ |
| 61228 | SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){ |
| 61229 | Vdbe *p; |
| 61230 | p = sqlite3DbMallocZero(db, sizeof(Vdbe) ); |
| 61231 | if( p==0 ) return 0; |
| 61232 | p->db = db; |
| 61233 | if( db->pVdbe ){ |
| @@ -61235,10 +61253,13 @@ | |
| 61235 | } |
| 61236 | p->pNext = db->pVdbe; |
| 61237 | p->pPrev = 0; |
| 61238 | db->pVdbe = p; |
| 61239 | p->magic = VDBE_MAGIC_INIT; |
| 61240 | return p; |
| 61241 | } |
| 61242 | |
| 61243 | /* |
| 61244 | ** Remember the SQL string for a prepared statement. |
| @@ -61354,10 +61375,19 @@ | |
| 61354 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 61355 | pOp->zComment = 0; |
| 61356 | #endif |
| 61357 | #ifdef SQLITE_DEBUG |
| 61358 | if( p->db->flags & SQLITE_VdbeAddopTrace ){ |
| 61359 | sqlite3VdbePrintOp(0, i, &p->aOp[i]); |
| 61360 | test_addop_breakpoint(); |
| 61361 | } |
| 61362 | #endif |
| 61363 | #ifdef VDBE_PROFILE |
| @@ -62075,11 +62105,21 @@ | |
| 62075 | if( c=='4' ) return pOp->p4.i; |
| 62076 | return pOp->p5; |
| 62077 | } |
| 62078 | |
| 62079 | /* |
| 62080 | ** Compute a string for the "comment" field of a VDBE opcode listing |
| 62081 | */ |
| 62082 | static int displayComment( |
| 62083 | const Op *pOp, /* The opcode to be commented */ |
| 62084 | const char *zP4, /* Previously obtained value for P4 */ |
| 62085 | char *zTemp, /* Write result here */ |
| @@ -62109,11 +62149,17 @@ | |
| 62109 | sqlite3_snprintf(nTemp-jj, zTemp+jj, "%d", v1); |
| 62110 | if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){ |
| 62111 | ii += 3; |
| 62112 | jj += sqlite3Strlen30(zTemp+jj); |
| 62113 | v2 = translateP(zSynopsis[ii], pOp); |
| 62114 | if( v2>1 ) sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1); |
| 62115 | }else if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){ |
| 62116 | ii += 4; |
| 62117 | } |
| 62118 | } |
| 62119 | jj += sqlite3Strlen30(zTemp+jj); |
| @@ -62341,10 +62387,13 @@ | |
| 62341 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 62342 | displayComment(pOp, zP4, zCom, sizeof(zCom)); |
| 62343 | #else |
| 62344 | zCom[0] = 0 |
| 62345 | #endif |
| 62346 | fprintf(pOut, zFormat1, pc, |
| 62347 | sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5, |
| 62348 | zCom |
| 62349 | ); |
| 62350 | fflush(pOut); |
| @@ -67401,11 +67450,11 @@ | |
| 67401 | }while( n-- ); |
| 67402 | break; |
| 67403 | } |
| 67404 | |
| 67405 | /* Opcode: Copy P1 P2 P3 * * |
| 67406 | ** Synopsis: r[P2@P3]=r[P1@P3] |
| 67407 | ** |
| 67408 | ** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. |
| 67409 | ** |
| 67410 | ** This instruction makes a deep copy of the value. A duplicate |
| 67411 | ** is made of any string or blob constant. See also OP_SCopy. |
| @@ -68744,11 +68793,11 @@ | |
| 68744 | /* This is the common case where the desired content fits on the original |
| 68745 | ** page - where the content is not on an overflow page */ |
| 68746 | VdbeMemRelease(pDest); |
| 68747 | sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], aType[p2], pDest); |
| 68748 | }else{ |
| 68749 | /* This branch happens only when content is on overflow pages */ |
| 68750 | t = aType[p2]; |
| 68751 | if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 |
| 68752 | && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) |
| 68753 | || (len = sqlite3VdbeSerialTypeLen(t))==0 |
| 68754 | ){ |
| @@ -72862,11 +72911,11 @@ | |
| 72862 | sqlite3BtreeLeaveAll(db); |
| 72863 | goto blob_open_out; |
| 72864 | } |
| 72865 | } |
| 72866 | |
| 72867 | pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db); |
| 72868 | assert( pBlob->pStmt || db->mallocFailed ); |
| 72869 | if( pBlob->pStmt ){ |
| 72870 | Vdbe *v = (Vdbe *)pBlob->pStmt; |
| 72871 | int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 72872 | |
| @@ -78416,10 +78465,15 @@ | |
| 78416 | ** added to the column cache after this call are removed when the |
| 78417 | ** corresponding pop occurs. |
| 78418 | */ |
| 78419 | SQLITE_PRIVATE void sqlite3ExprCachePush(Parse *pParse){ |
| 78420 | pParse->iCacheLevel++; |
| 78421 | } |
| 78422 | |
| 78423 | /* |
| 78424 | ** Remove from the column cache any entries that were added since the |
| 78425 | ** the previous N Push operations. In other words, restore the cache |
| @@ -78429,10 +78483,15 @@ | |
| 78429 | int i; |
| 78430 | struct yColCache *p; |
| 78431 | assert( N>0 ); |
| 78432 | assert( pParse->iCacheLevel>=N ); |
| 78433 | pParse->iCacheLevel -= N; |
| 78434 | for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ |
| 78435 | if( p->iReg && p->iLevel>pParse->iCacheLevel ){ |
| 78436 | cacheEntryClear(pParse, p); |
| 78437 | p->iReg = 0; |
| 78438 | } |
| @@ -78523,10 +78582,15 @@ | |
| 78523 | */ |
| 78524 | SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse *pParse){ |
| 78525 | int i; |
| 78526 | struct yColCache *p; |
| 78527 | |
| 78528 | for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ |
| 78529 | if( p->iReg ){ |
| 78530 | cacheEntryClear(pParse, p); |
| 78531 | p->iReg = 0; |
| 78532 | } |
| @@ -79659,11 +79723,21 @@ | |
| 79659 | if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){ |
| 79660 | sqlite3ExprCodeAtInit(pParse, pExpr, target+i, 0); |
| 79661 | }else{ |
| 79662 | int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); |
| 79663 | if( inReg!=target+i ){ |
| 79664 | sqlite3VdbeAddOp2(pParse->pVdbe, copyOp, inReg, target+i); |
| 79665 | } |
| 79666 | } |
| 79667 | } |
| 79668 | return n; |
| 79669 | } |
| @@ -83083,14 +83157,10 @@ | |
| 83083 | { |
| 83084 | int rc = SQLITE_OK; |
| 83085 | if( pExpr ){ |
| 83086 | if( pExpr->op!=TK_ID ){ |
| 83087 | rc = sqlite3ResolveExprNames(pName, pExpr); |
| 83088 | if( rc==SQLITE_OK && !sqlite3ExprIsConstant(pExpr) ){ |
| 83089 | sqlite3ErrorMsg(pName->pParse, "invalid name: \"%s\"", pExpr->u.zToken); |
| 83090 | return SQLITE_ERROR; |
| 83091 | } |
| 83092 | }else{ |
| 83093 | pExpr->op = TK_STRING; |
| 83094 | } |
| 83095 | } |
| 83096 | return rc; |
| @@ -89324,11 +89394,10 @@ | |
| 89324 | Vdbe *v = pParse->pVdbe; |
| 89325 | int j; |
| 89326 | Table *pTab = pIdx->pTable; |
| 89327 | int regBase; |
| 89328 | int nCol; |
| 89329 | Index *pPk; |
| 89330 | |
| 89331 | if( piPartIdxLabel ){ |
| 89332 | if( pIdx->pPartIdxWhere ){ |
| 89333 | *piPartIdxLabel = sqlite3VdbeMakeLabel(v); |
| 89334 | pParse->iPartIdxTab = iDataCur; |
| @@ -89338,20 +89407,13 @@ | |
| 89338 | *piPartIdxLabel = 0; |
| 89339 | } |
| 89340 | } |
| 89341 | nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn; |
| 89342 | regBase = sqlite3GetTempRange(pParse, nCol); |
| 89343 | pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); |
| 89344 | for(j=0; j<nCol; j++){ |
| 89345 | i16 idx = pIdx->aiColumn[j]; |
| 89346 | if( pPk ) idx = sqlite3ColumnOfIndex(pPk, idx); |
| 89347 | if( idx<0 || idx==pTab->iPKey ){ |
| 89348 | sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regBase+j); |
| 89349 | }else{ |
| 89350 | sqlite3VdbeAddOp3(v, OP_Column, iDataCur, idx, regBase+j); |
| 89351 | sqlite3ColumnDefault(v, pTab, pIdx->aiColumn[j], -1); |
| 89352 | } |
| 89353 | } |
| 89354 | if( regOut ){ |
| 89355 | const char *zAff; |
| 89356 | if( pTab->pSelect |
| 89357 | || OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt) |
| @@ -93982,51 +94044,53 @@ | |
| 93982 | sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk, |
| 93983 | regIdx, pIdx->nKeyCol); |
| 93984 | |
| 93985 | /* Generate code to handle collisions */ |
| 93986 | regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField); |
| 93987 | if( HasRowid(pTab) ){ |
| 93988 | sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); |
| 93989 | /* Conflict only if the rowid of the existing index entry |
| 93990 | ** is different from old-rowid */ |
| 93991 | if( isUpdate ){ |
| 93992 | sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData); |
| 93993 | } |
| 93994 | }else{ |
| 93995 | int x; |
| 93996 | /* Extract the PRIMARY KEY from the end of the index entry and |
| 93997 | ** store it in registers regR..regR+nPk-1 */ |
| 93998 | if( (isUpdate || onError==OE_Replace) && pIdx!=pPk ){ |
| 93999 | for(i=0; i<pPk->nKeyCol; i++){ |
| 94000 | x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]); |
| 94001 | sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); |
| 94002 | VdbeComment((v, "%s.%s", pTab->zName, |
| 94003 | pTab->aCol[pPk->aiColumn[i]].zName)); |
| 94004 | } |
| 94005 | } |
| 94006 | if( isUpdate ){ |
| 94007 | /* If currently processing the PRIMARY KEY of a WITHOUT ROWID |
| 94008 | ** table, only conflict if the new PRIMARY KEY values are actually |
| 94009 | ** different from the old. |
| 94010 | ** |
| 94011 | ** For a UNIQUE index, only conflict if the PRIMARY KEY values |
| 94012 | ** of the matched index row are different from the original PRIMARY |
| 94013 | ** KEY values of this row before the update. */ |
| 94014 | int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; |
| 94015 | int op = OP_Ne; |
| 94016 | int regCmp = (pIdx->autoIndex==2 ? regIdx : regR); |
| 94017 | |
| 94018 | for(i=0; i<pPk->nKeyCol; i++){ |
| 94019 | char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]); |
| 94020 | x = pPk->aiColumn[i]; |
| 94021 | if( i==(pPk->nKeyCol-1) ){ |
| 94022 | addrJump = addrUniqueOk; |
| 94023 | op = OP_Eq; |
| 94024 | } |
| 94025 | sqlite3VdbeAddOp4(v, op, |
| 94026 | regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ |
| 94027 | ); |
| 94028 | } |
| 94029 | } |
| 94030 | } |
| 94031 | |
| 94032 | /* Generate code that executes if the new index entry is not unique */ |
| @@ -99722,11 +99786,10 @@ | |
| 99722 | } |
| 99723 | }else if( eDest!=SRT_Exists ){ |
| 99724 | /* If the destination is an EXISTS(...) expression, the actual |
| 99725 | ** values returned by the SELECT are not required. |
| 99726 | */ |
| 99727 | sqlite3ExprCacheClear(pParse); |
| 99728 | sqlite3ExprCodeExprList(pParse, pEList, regResult, |
| 99729 | (eDest==SRT_Output)?SQLITE_ECEL_DUP:0); |
| 99730 | } |
| 99731 | nColumn = nResultCol; |
| 99732 | |
| @@ -100689,11 +100752,11 @@ | |
| 100689 | ** If an error occurs, return NULL and leave a message in pParse. |
| 100690 | */ |
| 100691 | SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){ |
| 100692 | Vdbe *v = pParse->pVdbe; |
| 100693 | if( v==0 ){ |
| 100694 | v = pParse->pVdbe = sqlite3VdbeCreate(pParse->db); |
| 100695 | #ifndef SQLITE_OMIT_TRACE |
| 100696 | if( v ){ |
| 100697 | sqlite3VdbeAddOp0(v, OP_Trace); |
| 100698 | } |
| 100699 | #endif |
| @@ -102947,18 +103010,27 @@ | |
| 102947 | */ |
| 102948 | static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ |
| 102949 | Vdbe *v = pParse->pVdbe; |
| 102950 | int i; |
| 102951 | struct AggInfo_func *pFunc; |
| 102952 | if( pAggInfo->nFunc+pAggInfo->nColumn==0 ){ |
| 102953 | return; |
| 102954 | } |
| 102955 | for(i=0; i<pAggInfo->nColumn; i++){ |
| 102956 | sqlite3VdbeAddOp2(v, OP_Null, 0, pAggInfo->aCol[i].iMem); |
| 102957 | } |
| 102958 | for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){ |
| 102959 | sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem); |
| 102960 | if( pFunc->iDistinct>=0 ){ |
| 102961 | Expr *pE = pFunc->pExpr; |
| 102962 | assert( !ExprHasProperty(pE, EP_xIsSelect) ); |
| 102963 | if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ |
| 102964 | sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " |
| @@ -103000,11 +103072,10 @@ | |
| 103000 | int addrHitTest = 0; |
| 103001 | struct AggInfo_func *pF; |
| 103002 | struct AggInfo_col *pC; |
| 103003 | |
| 103004 | pAggInfo->directMode = 1; |
| 103005 | sqlite3ExprCacheClear(pParse); |
| 103006 | for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){ |
| 103007 | int nArg; |
| 103008 | int addrNext = 0; |
| 103009 | int regAgg; |
| 103010 | ExprList *pList = pF->pExpr->x.pList; |
| @@ -103533,10 +103604,11 @@ | |
| 103533 | */ |
| 103534 | memset(&sNC, 0, sizeof(sNC)); |
| 103535 | sNC.pParse = pParse; |
| 103536 | sNC.pSrcList = pTabList; |
| 103537 | sNC.pAggInfo = &sAggInfo; |
| 103538 | sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0; |
| 103539 | sAggInfo.pGroupBy = pGroupBy; |
| 103540 | sqlite3ExprAnalyzeAggList(&sNC, pEList); |
| 103541 | sqlite3ExprAnalyzeAggList(&sNC, pOrderBy); |
| 103542 | if( pHaving ){ |
| @@ -103547,10 +103619,11 @@ | |
| 103547 | assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) ); |
| 103548 | sNC.ncFlags |= NC_InAggFunc; |
| 103549 | sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList); |
| 103550 | sNC.ncFlags &= ~NC_InAggFunc; |
| 103551 | } |
| 103552 | if( db->mallocFailed ) goto select_end; |
| 103553 | |
| 103554 | /* Processing for aggregates with GROUP BY is very different and |
| 103555 | ** much more complex than aggregates without a GROUP BY. |
| 103556 | */ |
| @@ -105451,11 +105524,11 @@ | |
| 105451 | pCol->affinity, &pValue); |
| 105452 | if( pValue ){ |
| 105453 | sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM); |
| 105454 | } |
| 105455 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 105456 | if( iReg>=0 && pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ |
| 105457 | sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); |
| 105458 | } |
| 105459 | #endif |
| 105460 | } |
| 105461 | } |
| @@ -105875,14 +105948,14 @@ | |
| 105875 | ** be used eliminates some redundant opcodes. |
| 105876 | */ |
| 105877 | newmask = sqlite3TriggerColmask( |
| 105878 | pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError |
| 105879 | ); |
| 105880 | sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1); |
| 105881 | for(i=0; i<pTab->nCol; i++){ |
| 105882 | if( i==pTab->iPKey ){ |
| 105883 | /*sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);*/ |
| 105884 | }else{ |
| 105885 | j = aXRef[i]; |
| 105886 | if( j>=0 ){ |
| 105887 | sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); |
| 105888 | }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<<i)) ){ |
| @@ -105892,10 +105965,12 @@ | |
| 105892 | ** a new.* reference in a trigger program. |
| 105893 | */ |
| 105894 | testcase( i==31 ); |
| 105895 | testcase( i==32 ); |
| 105896 | sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i); |
| 105897 | } |
| 105898 | } |
| 105899 | } |
| 105900 | |
| 105901 | /* Fire any BEFORE UPDATE triggers. This happens before constraints are |
| @@ -112004,10 +112079,11 @@ | |
| 112004 | */ |
| 112005 | if( pTerm==0 |
| 112006 | && saved_nEq==saved_nSkip |
| 112007 | && saved_nEq+1<pProbe->nKeyCol |
| 112008 | && pProbe->aiRowEst[saved_nEq+1]>=18 /* TUNING: Minimum for skip-scan */ |
| 112009 | ){ |
| 112010 | LogEst nIter; |
| 112011 | pNew->u.btree.nEq++; |
| 112012 | pNew->u.btree.nSkip++; |
| 112013 | pNew->aLTerm[pNew->nLTerm++] = 0; |
| @@ -119722,12 +119798,11 @@ | |
| 119722 | |
| 119723 | /* |
| 119724 | ** Return a static string containing the name corresponding to the error code |
| 119725 | ** specified in the argument. |
| 119726 | */ |
| 119727 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \ |
| 119728 | defined(SQLITE_DEBUG_OS_TRACE) |
| 119729 | SQLITE_PRIVATE const char *sqlite3ErrName(int rc){ |
| 119730 | const char *zName = 0; |
| 119731 | int i, origRc = rc; |
| 119732 | for(i=0; i<2 && zName==0; i++, rc &= 0xff){ |
| 119733 | switch( rc ){ |
| @@ -121300,10 +121375,12 @@ | |
| 121300 | #ifdef SQLITE_DEFAULT_LOCKING_MODE |
| 121301 | db->dfltLockMode = SQLITE_DEFAULT_LOCKING_MODE; |
| 121302 | sqlite3PagerLockingMode(sqlite3BtreePager(db->aDb[0].pBt), |
| 121303 | SQLITE_DEFAULT_LOCKING_MODE); |
| 121304 | #endif |
| 121305 | |
| 121306 | /* Enable the lookaside-malloc subsystem */ |
| 121307 | setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside, |
| 121308 | sqlite3GlobalConfig.nLookaside); |
| 121309 | |
| @@ -131632,61 +131709,74 @@ | |
| 131632 | } |
| 131633 | |
| 131634 | /* Step 2 */ |
| 131635 | switch( z[1] ){ |
| 131636 | case 'a': |
| 131637 | stem(&z, "lanoita", "ate", m_gt_0) || |
| 131638 | stem(&z, "lanoit", "tion", m_gt_0); |
| 131639 | break; |
| 131640 | case 'c': |
| 131641 | stem(&z, "icne", "ence", m_gt_0) || |
| 131642 | stem(&z, "icna", "ance", m_gt_0); |
| 131643 | break; |
| 131644 | case 'e': |
| 131645 | stem(&z, "rezi", "ize", m_gt_0); |
| 131646 | break; |
| 131647 | case 'g': |
| 131648 | stem(&z, "igol", "log", m_gt_0); |
| 131649 | break; |
| 131650 | case 'l': |
| 131651 | stem(&z, "ilb", "ble", m_gt_0) || |
| 131652 | stem(&z, "illa", "al", m_gt_0) || |
| 131653 | stem(&z, "iltne", "ent", m_gt_0) || |
| 131654 | stem(&z, "ile", "e", m_gt_0) || |
| 131655 | stem(&z, "ilsuo", "ous", m_gt_0); |
| 131656 | break; |
| 131657 | case 'o': |
| 131658 | stem(&z, "noitazi", "ize", m_gt_0) || |
| 131659 | stem(&z, "noita", "ate", m_gt_0) || |
| 131660 | stem(&z, "rota", "ate", m_gt_0); |
| 131661 | break; |
| 131662 | case 's': |
| 131663 | stem(&z, "msila", "al", m_gt_0) || |
| 131664 | stem(&z, "ssenevi", "ive", m_gt_0) || |
| 131665 | stem(&z, "ssenluf", "ful", m_gt_0) || |
| 131666 | stem(&z, "ssensuo", "ous", m_gt_0); |
| 131667 | break; |
| 131668 | case 't': |
| 131669 | stem(&z, "itila", "al", m_gt_0) || |
| 131670 | stem(&z, "itivi", "ive", m_gt_0) || |
| 131671 | stem(&z, "itilib", "ble", m_gt_0); |
| 131672 | break; |
| 131673 | } |
| 131674 | |
| 131675 | /* Step 3 */ |
| 131676 | switch( z[0] ){ |
| 131677 | case 'e': |
| 131678 | stem(&z, "etaci", "ic", m_gt_0) || |
| 131679 | stem(&z, "evita", "", m_gt_0) || |
| 131680 | stem(&z, "ezila", "al", m_gt_0); |
| 131681 | break; |
| 131682 | case 'i': |
| 131683 | stem(&z, "itici", "ic", m_gt_0); |
| 131684 | break; |
| 131685 | case 'l': |
| 131686 | stem(&z, "laci", "ic", m_gt_0) || |
| 131687 | stem(&z, "luf", "", m_gt_0); |
| 131688 | break; |
| 131689 | case 's': |
| 131690 | stem(&z, "ssen", "", m_gt_0); |
| 131691 | break; |
| 131692 | } |
| @@ -131723,13 +131813,15 @@ | |
| 131723 | if( z[2]=='a' ){ |
| 131724 | if( m_gt_1(z+3) ){ |
| 131725 | z += 3; |
| 131726 | } |
| 131727 | }else if( z[2]=='e' ){ |
| 131728 | stem(&z, "tneme", "", m_gt_1) || |
| 131729 | stem(&z, "tnem", "", m_gt_1) || |
| 131730 | stem(&z, "tne", "", m_gt_1); |
| 131731 | } |
| 131732 | } |
| 131733 | break; |
| 131734 | case 'o': |
| 131735 | if( z[0]=='u' ){ |
| @@ -131744,12 +131836,13 @@ | |
| 131744 | if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){ |
| 131745 | z += 3; |
| 131746 | } |
| 131747 | break; |
| 131748 | case 't': |
| 131749 | stem(&z, "eta", "", m_gt_1) || |
| 131750 | stem(&z, "iti", "", m_gt_1); |
| 131751 | break; |
| 131752 | case 'u': |
| 131753 | if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){ |
| 131754 | z += 3; |
| 131755 | } |
| 131756 |
| --- 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 | ** |
| @@ -9098,11 +9098,11 @@ | |
| 9098 | #define OP_String 25 /* synopsis: r[P2]='P4' (len=P1) */ |
| 9099 | #define OP_Null 26 /* synopsis: r[P2..P3]=NULL */ |
| 9100 | #define OP_Blob 27 /* synopsis: r[P2]=P4 (len=P1) */ |
| 9101 | #define OP_Variable 28 /* synopsis: r[P2]=parameter(P1,P4) */ |
| 9102 | #define OP_Move 29 /* synopsis: r[P2@P3]=r[P1@P3] */ |
| 9103 | #define OP_Copy 30 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ |
| 9104 | #define OP_SCopy 31 /* synopsis: r[P2]=r[P1] */ |
| 9105 | #define OP_ResultRow 32 /* synopsis: output=r[P1@P2] */ |
| 9106 | #define OP_CollSeq 33 |
| 9107 | #define OP_AddImm 34 /* synopsis: r[P1]=r[P1]+P2 */ |
| 9108 | #define OP_MustBeInt 35 |
| @@ -9263,11 +9263,11 @@ | |
| 9263 | |
| 9264 | /* |
| 9265 | ** Prototypes for the VDBE interface. See comments on the implementation |
| 9266 | ** for a description of what each of these routines does. |
| 9267 | */ |
| 9268 | SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse*); |
| 9269 | SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); |
| 9270 | SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); |
| 9271 | SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); |
| 9272 | SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); |
| 9273 | SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); |
| @@ -11003,10 +11003,11 @@ | |
| 11003 | u8 useSortingIdx; /* In direct mode, reference the sorting index rather |
| 11004 | ** than the source table */ |
| 11005 | int sortingIdx; /* Cursor number of the sorting index */ |
| 11006 | int sortingIdxPTab; /* Cursor number of pseudo-table */ |
| 11007 | int nSortingColumn; /* Number of columns in the sorting index */ |
| 11008 | int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */ |
| 11009 | ExprList *pGroupBy; /* The group by clause */ |
| 11010 | struct AggInfo_col { /* For each column used in source tables */ |
| 11011 | Table *pTab; /* Source table */ |
| 11012 | int iTable; /* Cursor number of the source table */ |
| 11013 | int iColumn; /* Column number within the source table */ |
| @@ -12452,12 +12453,11 @@ | |
| 12453 | SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...); |
| 12454 | SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); |
| 12455 | SQLITE_PRIVATE u8 sqlite3HexToInt(int h); |
| 12456 | SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); |
| 12457 | |
| 12458 | #if defined(SQLITE_TEST) |
| 12459 | SQLITE_PRIVATE const char *sqlite3ErrName(int); |
| 12460 | #endif |
| 12461 | |
| 12462 | SQLITE_PRIVATE const char *sqlite3ErrStr(int); |
| 12463 | SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); |
| @@ -13776,10 +13776,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 */ |
| @@ -15443,11 +15446,25 @@ | |
| 15446 | ** really care if the VFS receives and understands the information since it |
| 15447 | ** is only a hint and can be safely ignored. The sqlite3OsFileControlHint() |
| 15448 | ** routine has no return value since the return value would be meaningless. |
| 15449 | */ |
| 15450 | SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ |
| 15451 | #ifdef SQLITE_TEST |
| 15452 | if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){ |
| 15453 | /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite |
| 15454 | ** is using a regular VFS, it is called after the corresponding |
| 15455 | ** transaction has been committed. Injecting a fault at this point |
| 15456 | ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM |
| 15457 | ** but the transaction is committed anyway. |
| 15458 | ** |
| 15459 | ** The core must call OsFileControl() though, not OsFileControlHint(), |
| 15460 | ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably |
| 15461 | ** means the commit really has failed and an error should be returned |
| 15462 | ** to the user. */ |
| 15463 | DO_OS_MALLOC_TEST(id); |
| 15464 | } |
| 15465 | #endif |
| 15466 | return id->pMethods->xFileControl(id, op, pArg); |
| 15467 | } |
| 15468 | SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){ |
| 15469 | (void)id->pMethods->xFileControl(id, op, pArg); |
| 15470 | } |
| @@ -23098,11 +23115,11 @@ | |
| 23115 | /* 25 */ "String" OpHelp("r[P2]='P4' (len=P1)"), |
| 23116 | /* 26 */ "Null" OpHelp("r[P2..P3]=NULL"), |
| 23117 | /* 27 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), |
| 23118 | /* 28 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), |
| 23119 | /* 29 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), |
| 23120 | /* 30 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), |
| 23121 | /* 31 */ "SCopy" OpHelp("r[P2]=r[P1]"), |
| 23122 | /* 32 */ "ResultRow" OpHelp("output=r[P1@P2]"), |
| 23123 | /* 33 */ "CollSeq" OpHelp(""), |
| 23124 | /* 34 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), |
| 23125 | /* 35 */ "MustBeInt" OpHelp(""), |
| @@ -61223,11 +61240,12 @@ | |
| 61240 | */ |
| 61241 | |
| 61242 | /* |
| 61243 | ** Create a new virtual database engine. |
| 61244 | */ |
| 61245 | SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){ |
| 61246 | sqlite3 *db = pParse->db; |
| 61247 | Vdbe *p; |
| 61248 | p = sqlite3DbMallocZero(db, sizeof(Vdbe) ); |
| 61249 | if( p==0 ) return 0; |
| 61250 | p->db = db; |
| 61251 | if( db->pVdbe ){ |
| @@ -61235,10 +61253,13 @@ | |
| 61253 | } |
| 61254 | p->pNext = db->pVdbe; |
| 61255 | p->pPrev = 0; |
| 61256 | db->pVdbe = p; |
| 61257 | p->magic = VDBE_MAGIC_INIT; |
| 61258 | #if SQLITE_DEBUG |
| 61259 | p->pParse = pParse; |
| 61260 | #endif |
| 61261 | return p; |
| 61262 | } |
| 61263 | |
| 61264 | /* |
| 61265 | ** Remember the SQL string for a prepared statement. |
| @@ -61354,10 +61375,19 @@ | |
| 61375 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 61376 | pOp->zComment = 0; |
| 61377 | #endif |
| 61378 | #ifdef SQLITE_DEBUG |
| 61379 | if( p->db->flags & SQLITE_VdbeAddopTrace ){ |
| 61380 | int jj, kk; |
| 61381 | Parse *pParse = p->pParse; |
| 61382 | for(jj=kk=0; jj<SQLITE_N_COLCACHE; jj++){ |
| 61383 | struct yColCache *x = pParse->aColCache + jj; |
| 61384 | if( x->iLevel>pParse->iCacheLevel || x->iReg==0 ) continue; |
| 61385 | printf(" r[%d]={%d:%d}", x->iReg, x->iTable, x->iColumn); |
| 61386 | kk++; |
| 61387 | } |
| 61388 | if( kk ) printf("\n"); |
| 61389 | sqlite3VdbePrintOp(0, i, &p->aOp[i]); |
| 61390 | test_addop_breakpoint(); |
| 61391 | } |
| 61392 | #endif |
| 61393 | #ifdef VDBE_PROFILE |
| @@ -62075,11 +62105,21 @@ | |
| 62105 | if( c=='4' ) return pOp->p4.i; |
| 62106 | return pOp->p5; |
| 62107 | } |
| 62108 | |
| 62109 | /* |
| 62110 | ** Compute a string for the "comment" field of a VDBE opcode listing. |
| 62111 | ** |
| 62112 | ** The Synopsis: field in comments in the vdbe.c source file gets converted |
| 62113 | ** to an extra string that is appended to the sqlite3OpcodeName(). In the |
| 62114 | ** absence of other comments, this synopsis becomes the comment on the opcode. |
| 62115 | ** Some translation occurs: |
| 62116 | ** |
| 62117 | ** "PX" -> "r[X]" |
| 62118 | ** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1 |
| 62119 | ** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0 |
| 62120 | ** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x |
| 62121 | */ |
| 62122 | static int displayComment( |
| 62123 | const Op *pOp, /* The opcode to be commented */ |
| 62124 | const char *zP4, /* Previously obtained value for P4 */ |
| 62125 | char *zTemp, /* Write result here */ |
| @@ -62109,11 +62149,17 @@ | |
| 62149 | sqlite3_snprintf(nTemp-jj, zTemp+jj, "%d", v1); |
| 62150 | if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){ |
| 62151 | ii += 3; |
| 62152 | jj += sqlite3Strlen30(zTemp+jj); |
| 62153 | v2 = translateP(zSynopsis[ii], pOp); |
| 62154 | if( strncmp(zSynopsis+ii+1,"+1",2)==0 ){ |
| 62155 | ii += 2; |
| 62156 | v2++; |
| 62157 | } |
| 62158 | if( v2>1 ){ |
| 62159 | sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1); |
| 62160 | } |
| 62161 | }else if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){ |
| 62162 | ii += 4; |
| 62163 | } |
| 62164 | } |
| 62165 | jj += sqlite3Strlen30(zTemp+jj); |
| @@ -62341,10 +62387,13 @@ | |
| 62387 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 62388 | displayComment(pOp, zP4, zCom, sizeof(zCom)); |
| 62389 | #else |
| 62390 | zCom[0] = 0 |
| 62391 | #endif |
| 62392 | /* NB: The sqlite3OpcodeName() function is implemented by code created |
| 62393 | ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the |
| 62394 | ** information from the vdbe.c source text */ |
| 62395 | fprintf(pOut, zFormat1, pc, |
| 62396 | sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5, |
| 62397 | zCom |
| 62398 | ); |
| 62399 | fflush(pOut); |
| @@ -67401,11 +67450,11 @@ | |
| 67450 | }while( n-- ); |
| 67451 | break; |
| 67452 | } |
| 67453 | |
| 67454 | /* Opcode: Copy P1 P2 P3 * * |
| 67455 | ** Synopsis: r[P2@P3+1]=r[P1@P3+1] |
| 67456 | ** |
| 67457 | ** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. |
| 67458 | ** |
| 67459 | ** This instruction makes a deep copy of the value. A duplicate |
| 67460 | ** is made of any string or blob constant. See also OP_SCopy. |
| @@ -68744,11 +68793,11 @@ | |
| 68793 | /* This is the common case where the desired content fits on the original |
| 68794 | ** page - where the content is not on an overflow page */ |
| 68795 | VdbeMemRelease(pDest); |
| 68796 | sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], aType[p2], pDest); |
| 68797 | }else{ |
| 68798 | /* This branch happens only when content is on overflow pages */ |
| 68799 | t = aType[p2]; |
| 68800 | if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 |
| 68801 | && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) |
| 68802 | || (len = sqlite3VdbeSerialTypeLen(t))==0 |
| 68803 | ){ |
| @@ -72862,11 +72911,11 @@ | |
| 72911 | sqlite3BtreeLeaveAll(db); |
| 72912 | goto blob_open_out; |
| 72913 | } |
| 72914 | } |
| 72915 | |
| 72916 | pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse); |
| 72917 | assert( pBlob->pStmt || db->mallocFailed ); |
| 72918 | if( pBlob->pStmt ){ |
| 72919 | Vdbe *v = (Vdbe *)pBlob->pStmt; |
| 72920 | int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 72921 | |
| @@ -78416,10 +78465,15 @@ | |
| 78465 | ** added to the column cache after this call are removed when the |
| 78466 | ** corresponding pop occurs. |
| 78467 | */ |
| 78468 | SQLITE_PRIVATE void sqlite3ExprCachePush(Parse *pParse){ |
| 78469 | pParse->iCacheLevel++; |
| 78470 | #ifdef SQLITE_DEBUG |
| 78471 | if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ |
| 78472 | printf("PUSH to %d\n", pParse->iCacheLevel); |
| 78473 | } |
| 78474 | #endif |
| 78475 | } |
| 78476 | |
| 78477 | /* |
| 78478 | ** Remove from the column cache any entries that were added since the |
| 78479 | ** the previous N Push operations. In other words, restore the cache |
| @@ -78429,10 +78483,15 @@ | |
| 78483 | int i; |
| 78484 | struct yColCache *p; |
| 78485 | assert( N>0 ); |
| 78486 | assert( pParse->iCacheLevel>=N ); |
| 78487 | pParse->iCacheLevel -= N; |
| 78488 | #ifdef SQLITE_DEBUG |
| 78489 | if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ |
| 78490 | printf("POP to %d\n", pParse->iCacheLevel); |
| 78491 | } |
| 78492 | #endif |
| 78493 | for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ |
| 78494 | if( p->iReg && p->iLevel>pParse->iCacheLevel ){ |
| 78495 | cacheEntryClear(pParse, p); |
| 78496 | p->iReg = 0; |
| 78497 | } |
| @@ -78523,10 +78582,15 @@ | |
| 78582 | */ |
| 78583 | SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse *pParse){ |
| 78584 | int i; |
| 78585 | struct yColCache *p; |
| 78586 | |
| 78587 | #if SQLITE_DEBUG |
| 78588 | if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ |
| 78589 | printf("CLEAR\n"); |
| 78590 | } |
| 78591 | #endif |
| 78592 | for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ |
| 78593 | if( p->iReg ){ |
| 78594 | cacheEntryClear(pParse, p); |
| 78595 | p->iReg = 0; |
| 78596 | } |
| @@ -79659,11 +79723,21 @@ | |
| 79723 | if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){ |
| 79724 | sqlite3ExprCodeAtInit(pParse, pExpr, target+i, 0); |
| 79725 | }else{ |
| 79726 | int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); |
| 79727 | if( inReg!=target+i ){ |
| 79728 | VdbeOp *pOp; |
| 79729 | Vdbe *v = pParse->pVdbe; |
| 79730 | if( copyOp==OP_Copy |
| 79731 | && (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy |
| 79732 | && pOp->p1+pOp->p3+1==inReg |
| 79733 | && pOp->p2+pOp->p3+1==target+i |
| 79734 | ){ |
| 79735 | pOp->p3++; |
| 79736 | }else{ |
| 79737 | sqlite3VdbeAddOp2(v, copyOp, inReg, target+i); |
| 79738 | } |
| 79739 | } |
| 79740 | } |
| 79741 | } |
| 79742 | return n; |
| 79743 | } |
| @@ -83083,14 +83157,10 @@ | |
| 83157 | { |
| 83158 | int rc = SQLITE_OK; |
| 83159 | if( pExpr ){ |
| 83160 | if( pExpr->op!=TK_ID ){ |
| 83161 | rc = sqlite3ResolveExprNames(pName, pExpr); |
| 83162 | }else{ |
| 83163 | pExpr->op = TK_STRING; |
| 83164 | } |
| 83165 | } |
| 83166 | return rc; |
| @@ -89324,11 +89394,10 @@ | |
| 89394 | Vdbe *v = pParse->pVdbe; |
| 89395 | int j; |
| 89396 | Table *pTab = pIdx->pTable; |
| 89397 | int regBase; |
| 89398 | int nCol; |
| 89399 | |
| 89400 | if( piPartIdxLabel ){ |
| 89401 | if( pIdx->pPartIdxWhere ){ |
| 89402 | *piPartIdxLabel = sqlite3VdbeMakeLabel(v); |
| 89403 | pParse->iPartIdxTab = iDataCur; |
| @@ -89338,20 +89407,13 @@ | |
| 89407 | *piPartIdxLabel = 0; |
| 89408 | } |
| 89409 | } |
| 89410 | nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn; |
| 89411 | regBase = sqlite3GetTempRange(pParse, nCol); |
| 89412 | for(j=0; j<nCol; j++){ |
| 89413 | sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j], |
| 89414 | regBase+j); |
| 89415 | } |
| 89416 | if( regOut ){ |
| 89417 | const char *zAff; |
| 89418 | if( pTab->pSelect |
| 89419 | || OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt) |
| @@ -93982,51 +94044,53 @@ | |
| 94044 | sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk, |
| 94045 | regIdx, pIdx->nKeyCol); |
| 94046 | |
| 94047 | /* Generate code to handle collisions */ |
| 94048 | regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField); |
| 94049 | if( isUpdate || onError==OE_Replace ){ |
| 94050 | if( HasRowid(pTab) ){ |
| 94051 | sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); |
| 94052 | /* Conflict only if the rowid of the existing index entry |
| 94053 | ** is different from old-rowid */ |
| 94054 | if( isUpdate ){ |
| 94055 | sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData); |
| 94056 | } |
| 94057 | }else{ |
| 94058 | int x; |
| 94059 | /* Extract the PRIMARY KEY from the end of the index entry and |
| 94060 | ** store it in registers regR..regR+nPk-1 */ |
| 94061 | if( pIdx!=pPk ){ |
| 94062 | for(i=0; i<pPk->nKeyCol; i++){ |
| 94063 | x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]); |
| 94064 | sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); |
| 94065 | VdbeComment((v, "%s.%s", pTab->zName, |
| 94066 | pTab->aCol[pPk->aiColumn[i]].zName)); |
| 94067 | } |
| 94068 | } |
| 94069 | if( isUpdate ){ |
| 94070 | /* If currently processing the PRIMARY KEY of a WITHOUT ROWID |
| 94071 | ** table, only conflict if the new PRIMARY KEY values are actually |
| 94072 | ** different from the old. |
| 94073 | ** |
| 94074 | ** For a UNIQUE index, only conflict if the PRIMARY KEY values |
| 94075 | ** of the matched index row are different from the original PRIMARY |
| 94076 | ** KEY values of this row before the update. */ |
| 94077 | int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; |
| 94078 | int op = OP_Ne; |
| 94079 | int regCmp = (pIdx->autoIndex==2 ? regIdx : regR); |
| 94080 | |
| 94081 | for(i=0; i<pPk->nKeyCol; i++){ |
| 94082 | char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]); |
| 94083 | x = pPk->aiColumn[i]; |
| 94084 | if( i==(pPk->nKeyCol-1) ){ |
| 94085 | addrJump = addrUniqueOk; |
| 94086 | op = OP_Eq; |
| 94087 | } |
| 94088 | sqlite3VdbeAddOp4(v, op, |
| 94089 | regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ |
| 94090 | ); |
| 94091 | } |
| 94092 | } |
| 94093 | } |
| 94094 | } |
| 94095 | |
| 94096 | /* Generate code that executes if the new index entry is not unique */ |
| @@ -99722,11 +99786,10 @@ | |
| 99786 | } |
| 99787 | }else if( eDest!=SRT_Exists ){ |
| 99788 | /* If the destination is an EXISTS(...) expression, the actual |
| 99789 | ** values returned by the SELECT are not required. |
| 99790 | */ |
| 99791 | sqlite3ExprCodeExprList(pParse, pEList, regResult, |
| 99792 | (eDest==SRT_Output)?SQLITE_ECEL_DUP:0); |
| 99793 | } |
| 99794 | nColumn = nResultCol; |
| 99795 | |
| @@ -100689,11 +100752,11 @@ | |
| 100752 | ** If an error occurs, return NULL and leave a message in pParse. |
| 100753 | */ |
| 100754 | SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){ |
| 100755 | Vdbe *v = pParse->pVdbe; |
| 100756 | if( v==0 ){ |
| 100757 | v = pParse->pVdbe = sqlite3VdbeCreate(pParse); |
| 100758 | #ifndef SQLITE_OMIT_TRACE |
| 100759 | if( v ){ |
| 100760 | sqlite3VdbeAddOp0(v, OP_Trace); |
| 100761 | } |
| 100762 | #endif |
| @@ -102947,18 +103010,27 @@ | |
| 103010 | */ |
| 103011 | static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ |
| 103012 | Vdbe *v = pParse->pVdbe; |
| 103013 | int i; |
| 103014 | struct AggInfo_func *pFunc; |
| 103015 | int nReg = pAggInfo->nFunc + pAggInfo->nColumn; |
| 103016 | if( nReg==0 ) return; |
| 103017 | #ifdef SQLITE_DEBUG |
| 103018 | /* Verify that all AggInfo registers are within the range specified by |
| 103019 | ** AggInfo.mnReg..AggInfo.mxReg */ |
| 103020 | assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 ); |
| 103021 | for(i=0; i<pAggInfo->nColumn; i++){ |
| 103022 | assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg |
| 103023 | && pAggInfo->aCol[i].iMem<=pAggInfo->mxReg ); |
| 103024 | } |
| 103025 | for(i=0; i<pAggInfo->nFunc; i++){ |
| 103026 | assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg |
| 103027 | && pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg ); |
| 103028 | } |
| 103029 | #endif |
| 103030 | sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg); |
| 103031 | for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){ |
| 103032 | if( pFunc->iDistinct>=0 ){ |
| 103033 | Expr *pE = pFunc->pExpr; |
| 103034 | assert( !ExprHasProperty(pE, EP_xIsSelect) ); |
| 103035 | if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ |
| 103036 | sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " |
| @@ -103000,11 +103072,10 @@ | |
| 103072 | int addrHitTest = 0; |
| 103073 | struct AggInfo_func *pF; |
| 103074 | struct AggInfo_col *pC; |
| 103075 | |
| 103076 | pAggInfo->directMode = 1; |
| 103077 | for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){ |
| 103078 | int nArg; |
| 103079 | int addrNext = 0; |
| 103080 | int regAgg; |
| 103081 | ExprList *pList = pF->pExpr->x.pList; |
| @@ -103533,10 +103604,11 @@ | |
| 103604 | */ |
| 103605 | memset(&sNC, 0, sizeof(sNC)); |
| 103606 | sNC.pParse = pParse; |
| 103607 | sNC.pSrcList = pTabList; |
| 103608 | sNC.pAggInfo = &sAggInfo; |
| 103609 | sAggInfo.mnReg = pParse->nMem+1; |
| 103610 | sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0; |
| 103611 | sAggInfo.pGroupBy = pGroupBy; |
| 103612 | sqlite3ExprAnalyzeAggList(&sNC, pEList); |
| 103613 | sqlite3ExprAnalyzeAggList(&sNC, pOrderBy); |
| 103614 | if( pHaving ){ |
| @@ -103547,10 +103619,11 @@ | |
| 103619 | assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) ); |
| 103620 | sNC.ncFlags |= NC_InAggFunc; |
| 103621 | sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList); |
| 103622 | sNC.ncFlags &= ~NC_InAggFunc; |
| 103623 | } |
| 103624 | sAggInfo.mxReg = pParse->nMem; |
| 103625 | if( db->mallocFailed ) goto select_end; |
| 103626 | |
| 103627 | /* Processing for aggregates with GROUP BY is very different and |
| 103628 | ** much more complex than aggregates without a GROUP BY. |
| 103629 | */ |
| @@ -105451,11 +105524,11 @@ | |
| 105524 | pCol->affinity, &pValue); |
| 105525 | if( pValue ){ |
| 105526 | sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM); |
| 105527 | } |
| 105528 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 105529 | if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ |
| 105530 | sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); |
| 105531 | } |
| 105532 | #endif |
| 105533 | } |
| 105534 | } |
| @@ -105875,14 +105948,14 @@ | |
| 105948 | ** be used eliminates some redundant opcodes. |
| 105949 | */ |
| 105950 | newmask = sqlite3TriggerColmask( |
| 105951 | pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError |
| 105952 | ); |
| 105953 | /*sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);*/ |
| 105954 | for(i=0; i<pTab->nCol; i++){ |
| 105955 | if( i==pTab->iPKey ){ |
| 105956 | sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); |
| 105957 | }else{ |
| 105958 | j = aXRef[i]; |
| 105959 | if( j>=0 ){ |
| 105960 | sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); |
| 105961 | }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<<i)) ){ |
| @@ -105892,10 +105965,12 @@ | |
| 105965 | ** a new.* reference in a trigger program. |
| 105966 | */ |
| 105967 | testcase( i==31 ); |
| 105968 | testcase( i==32 ); |
| 105969 | sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i); |
| 105970 | }else{ |
| 105971 | sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); |
| 105972 | } |
| 105973 | } |
| 105974 | } |
| 105975 | |
| 105976 | /* Fire any BEFORE UPDATE triggers. This happens before constraints are |
| @@ -112004,10 +112079,11 @@ | |
| 112079 | */ |
| 112080 | if( pTerm==0 |
| 112081 | && saved_nEq==saved_nSkip |
| 112082 | && saved_nEq+1<pProbe->nKeyCol |
| 112083 | && pProbe->aiRowEst[saved_nEq+1]>=18 /* TUNING: Minimum for skip-scan */ |
| 112084 | && (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK |
| 112085 | ){ |
| 112086 | LogEst nIter; |
| 112087 | pNew->u.btree.nEq++; |
| 112088 | pNew->u.btree.nSkip++; |
| 112089 | pNew->aLTerm[pNew->nLTerm++] = 0; |
| @@ -119722,12 +119798,11 @@ | |
| 119798 | |
| 119799 | /* |
| 119800 | ** Return a static string containing the name corresponding to the error code |
| 119801 | ** specified in the argument. |
| 119802 | */ |
| 119803 | #if defined(SQLITE_TEST) |
| 119804 | SQLITE_PRIVATE const char *sqlite3ErrName(int rc){ |
| 119805 | const char *zName = 0; |
| 119806 | int i, origRc = rc; |
| 119807 | for(i=0; i<2 && zName==0; i++, rc &= 0xff){ |
| 119808 | switch( rc ){ |
| @@ -121300,10 +121375,12 @@ | |
| 121375 | #ifdef SQLITE_DEFAULT_LOCKING_MODE |
| 121376 | db->dfltLockMode = SQLITE_DEFAULT_LOCKING_MODE; |
| 121377 | sqlite3PagerLockingMode(sqlite3BtreePager(db->aDb[0].pBt), |
| 121378 | SQLITE_DEFAULT_LOCKING_MODE); |
| 121379 | #endif |
| 121380 | |
| 121381 | if( rc ) sqlite3Error(db, rc, 0); |
| 121382 | |
| 121383 | /* Enable the lookaside-malloc subsystem */ |
| 121384 | setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside, |
| 121385 | sqlite3GlobalConfig.nLookaside); |
| 121386 | |
| @@ -131632,61 +131709,74 @@ | |
| 131709 | } |
| 131710 | |
| 131711 | /* Step 2 */ |
| 131712 | switch( z[1] ){ |
| 131713 | case 'a': |
| 131714 | if( !stem(&z, "lanoita", "ate", m_gt_0) ){ |
| 131715 | stem(&z, "lanoit", "tion", m_gt_0); |
| 131716 | } |
| 131717 | break; |
| 131718 | case 'c': |
| 131719 | if( !stem(&z, "icne", "ence", m_gt_0) ){ |
| 131720 | stem(&z, "icna", "ance", m_gt_0); |
| 131721 | } |
| 131722 | break; |
| 131723 | case 'e': |
| 131724 | stem(&z, "rezi", "ize", m_gt_0); |
| 131725 | break; |
| 131726 | case 'g': |
| 131727 | stem(&z, "igol", "log", m_gt_0); |
| 131728 | break; |
| 131729 | case 'l': |
| 131730 | if( !stem(&z, "ilb", "ble", m_gt_0) |
| 131731 | && !stem(&z, "illa", "al", m_gt_0) |
| 131732 | && !stem(&z, "iltne", "ent", m_gt_0) |
| 131733 | && !stem(&z, "ile", "e", m_gt_0) |
| 131734 | ){ |
| 131735 | stem(&z, "ilsuo", "ous", m_gt_0); |
| 131736 | } |
| 131737 | break; |
| 131738 | case 'o': |
| 131739 | if( !stem(&z, "noitazi", "ize", m_gt_0) |
| 131740 | && !stem(&z, "noita", "ate", m_gt_0) |
| 131741 | ){ |
| 131742 | stem(&z, "rota", "ate", m_gt_0); |
| 131743 | } |
| 131744 | break; |
| 131745 | case 's': |
| 131746 | if( !stem(&z, "msila", "al", m_gt_0) |
| 131747 | && !stem(&z, "ssenevi", "ive", m_gt_0) |
| 131748 | && !stem(&z, "ssenluf", "ful", m_gt_0) |
| 131749 | ){ |
| 131750 | stem(&z, "ssensuo", "ous", m_gt_0); |
| 131751 | } |
| 131752 | break; |
| 131753 | case 't': |
| 131754 | if( !stem(&z, "itila", "al", m_gt_0) |
| 131755 | && !stem(&z, "itivi", "ive", m_gt_0) |
| 131756 | ){ |
| 131757 | stem(&z, "itilib", "ble", m_gt_0); |
| 131758 | } |
| 131759 | break; |
| 131760 | } |
| 131761 | |
| 131762 | /* Step 3 */ |
| 131763 | switch( z[0] ){ |
| 131764 | case 'e': |
| 131765 | if( !stem(&z, "etaci", "ic", m_gt_0) |
| 131766 | && !stem(&z, "evita", "", m_gt_0) |
| 131767 | ){ |
| 131768 | stem(&z, "ezila", "al", m_gt_0); |
| 131769 | } |
| 131770 | break; |
| 131771 | case 'i': |
| 131772 | stem(&z, "itici", "ic", m_gt_0); |
| 131773 | break; |
| 131774 | case 'l': |
| 131775 | if( !stem(&z, "laci", "ic", m_gt_0) ){ |
| 131776 | stem(&z, "luf", "", m_gt_0); |
| 131777 | } |
| 131778 | break; |
| 131779 | case 's': |
| 131780 | stem(&z, "ssen", "", m_gt_0); |
| 131781 | break; |
| 131782 | } |
| @@ -131723,13 +131813,15 @@ | |
| 131813 | if( z[2]=='a' ){ |
| 131814 | if( m_gt_1(z+3) ){ |
| 131815 | z += 3; |
| 131816 | } |
| 131817 | }else if( z[2]=='e' ){ |
| 131818 | if( !stem(&z, "tneme", "", m_gt_1) |
| 131819 | && !stem(&z, "tnem", "", m_gt_1) |
| 131820 | ){ |
| 131821 | stem(&z, "tne", "", m_gt_1); |
| 131822 | } |
| 131823 | } |
| 131824 | } |
| 131825 | break; |
| 131826 | case 'o': |
| 131827 | if( z[0]=='u' ){ |
| @@ -131744,12 +131836,13 @@ | |
| 131836 | if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){ |
| 131837 | z += 3; |
| 131838 | } |
| 131839 | break; |
| 131840 | case 't': |
| 131841 | if( !stem(&z, "eta", "", m_gt_1) ){ |
| 131842 | stem(&z, "iti", "", m_gt_1); |
| 131843 | } |
| 131844 | break; |
| 131845 | case 'u': |
| 131846 | if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){ |
| 131847 | z += 3; |
| 131848 | } |
| 131849 |
+1
-1
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -107,11 +107,11 @@ | ||
| 107 | 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | 109 | */ |
| 110 | 110 | #define SQLITE_VERSION "3.8.3" |
| 111 | 111 | #define SQLITE_VERSION_NUMBER 3008003 |
| 112 | -#define SQLITE_SOURCE_ID "2013-12-17 16:32:56 93121d3097a43997af3c0de65bd9bd7663845fa2" | |
| 112 | +#define SQLITE_SOURCE_ID "2013-12-23 11:33:32 25b8a1c9ba77df3b7c78cbce922cb593d661696d" | |
| 113 | 113 | |
| 114 | 114 | /* |
| 115 | 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | 117 | ** |
| 118 | 118 |
| --- 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-17 16:32:56 93121d3097a43997af3c0de65bd9bd7663845fa2" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| 118 |
| --- 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 | ** |
| 118 |
+1
-1
| --- src/tag.c | ||
| +++ src/tag.c | ||
| @@ -326,11 +326,11 @@ | ||
| 326 | 326 | } |
| 327 | 327 | blob_appendf(&ctrl, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); |
| 328 | 328 | md5sum_blob(&ctrl, &cksum); |
| 329 | 329 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 330 | 330 | nrid = content_put(&ctrl); |
| 331 | - manifest_crosslink(nrid, &ctrl); | |
| 331 | + manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); | |
| 332 | 332 | assert( blob_is_reset(&ctrl) ); |
| 333 | 333 | } |
| 334 | 334 | |
| 335 | 335 | /* |
| 336 | 336 | ** COMMAND: tag |
| 337 | 337 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -326,11 +326,11 @@ | |
| 326 | } |
| 327 | blob_appendf(&ctrl, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); |
| 328 | md5sum_blob(&ctrl, &cksum); |
| 329 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 330 | nrid = content_put(&ctrl); |
| 331 | manifest_crosslink(nrid, &ctrl); |
| 332 | assert( blob_is_reset(&ctrl) ); |
| 333 | } |
| 334 | |
| 335 | /* |
| 336 | ** COMMAND: tag |
| 337 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -326,11 +326,11 @@ | |
| 326 | } |
| 327 | blob_appendf(&ctrl, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); |
| 328 | md5sum_blob(&ctrl, &cksum); |
| 329 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 330 | nrid = content_put(&ctrl); |
| 331 | manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); |
| 332 | assert( blob_is_reset(&ctrl) ); |
| 333 | } |
| 334 | |
| 335 | /* |
| 336 | ** COMMAND: tag |
| 337 |
+1
-1
| --- src/tar.c | ||
| +++ src/tar.c | ||
| @@ -427,11 +427,11 @@ | ||
| 427 | 427 | Blob file; |
| 428 | 428 | if( g.argc<3 ){ |
| 429 | 429 | usage("ARCHIVE FILE...."); |
| 430 | 430 | } |
| 431 | 431 | sqlite3_open(":memory:", &g.db); |
| 432 | - tar_begin(0); | |
| 432 | + tar_begin(-1); | |
| 433 | 433 | for(i=3; i<g.argc; i++){ |
| 434 | 434 | blob_zero(&file); |
| 435 | 435 | blob_read_from_file(&file, g.argv[i]); |
| 436 | 436 | tar_add_file(g.argv[i], &file, |
| 437 | 437 | file_wd_perm(g.argv[i]), file_wd_mtime(g.argv[i])); |
| 438 | 438 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -427,11 +427,11 @@ | |
| 427 | Blob file; |
| 428 | if( g.argc<3 ){ |
| 429 | usage("ARCHIVE FILE...."); |
| 430 | } |
| 431 | sqlite3_open(":memory:", &g.db); |
| 432 | tar_begin(0); |
| 433 | for(i=3; i<g.argc; i++){ |
| 434 | blob_zero(&file); |
| 435 | blob_read_from_file(&file, g.argv[i]); |
| 436 | tar_add_file(g.argv[i], &file, |
| 437 | file_wd_perm(g.argv[i]), file_wd_mtime(g.argv[i])); |
| 438 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -427,11 +427,11 @@ | |
| 427 | Blob file; |
| 428 | if( g.argc<3 ){ |
| 429 | usage("ARCHIVE FILE...."); |
| 430 | } |
| 431 | sqlite3_open(":memory:", &g.db); |
| 432 | tar_begin(-1); |
| 433 | for(i=3; i<g.argc; i++){ |
| 434 | blob_zero(&file); |
| 435 | blob_read_from_file(&file, g.argv[i]); |
| 436 | tar_add_file(g.argv[i], &file, |
| 437 | file_wd_perm(g.argv[i]), file_wd_mtime(g.argv[i])); |
| 438 |
M
src/th.c
+9
-3
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -1876,18 +1876,18 @@ | ||
| 1876 | 1876 | if( rc==TH_OK && eArgType==ARG_INTEGER ){ |
| 1877 | 1877 | int iRes = 0; |
| 1878 | 1878 | switch( pExpr->pOp->eOp ) { |
| 1879 | 1879 | case OP_MULTIPLY: iRes = iLeft*iRight; break; |
| 1880 | 1880 | case OP_DIVIDE: |
| 1881 | - if(!iRight){ | |
| 1881 | + if( !iRight ){ | |
| 1882 | 1882 | Th_ErrorMessage(interp, "Divide by 0:", zLeft, nLeft); |
| 1883 | 1883 | return TH_ERROR; |
| 1884 | 1884 | } |
| 1885 | 1885 | iRes = iLeft/iRight; |
| 1886 | 1886 | break; |
| 1887 | 1887 | case OP_MODULUS: |
| 1888 | - if(!iRight){ | |
| 1888 | + if( !iRight ){ | |
| 1889 | 1889 | Th_ErrorMessage(interp, "Modulo by 0:", zLeft, nLeft); |
| 1890 | 1890 | return TH_ERROR; |
| 1891 | 1891 | } |
| 1892 | 1892 | iRes = iLeft%iRight; |
| 1893 | 1893 | break; |
| @@ -1913,11 +1913,17 @@ | ||
| 1913 | 1913 | } |
| 1914 | 1914 | Th_SetResultInt(interp, iRes); |
| 1915 | 1915 | }else if( rc==TH_OK && eArgType==ARG_NUMBER ){ |
| 1916 | 1916 | switch( pExpr->pOp->eOp ) { |
| 1917 | 1917 | case OP_MULTIPLY: Th_SetResultDouble(interp, fLeft*fRight); break; |
| 1918 | - case OP_DIVIDE: Th_SetResultDouble(interp, fLeft/fRight); break; | |
| 1918 | + case OP_DIVIDE: | |
| 1919 | + if( fRight==0.0 ){ | |
| 1920 | + Th_ErrorMessage(interp, "Divide by 0:", zLeft, nLeft); | |
| 1921 | + return TH_ERROR; | |
| 1922 | + } | |
| 1923 | + Th_SetResultDouble(interp, fLeft/fRight); | |
| 1924 | + break; | |
| 1919 | 1925 | case OP_ADD: Th_SetResultDouble(interp, fLeft+fRight); break; |
| 1920 | 1926 | case OP_SUBTRACT: Th_SetResultDouble(interp, fLeft-fRight); break; |
| 1921 | 1927 | case OP_LT: Th_SetResultInt(interp, fLeft<fRight); break; |
| 1922 | 1928 | case OP_GT: Th_SetResultInt(interp, fLeft>fRight); break; |
| 1923 | 1929 | case OP_LE: Th_SetResultInt(interp, fLeft<=fRight); break; |
| 1924 | 1930 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -1876,18 +1876,18 @@ | |
| 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; |
| @@ -1913,11 +1913,17 @@ | |
| 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; |
| 1924 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -1876,18 +1876,18 @@ | |
| 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; |
| @@ -1913,11 +1913,17 @@ | |
| 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: |
| 1919 | if( fRight==0.0 ){ |
| 1920 | Th_ErrorMessage(interp, "Divide by 0:", zLeft, nLeft); |
| 1921 | return TH_ERROR; |
| 1922 | } |
| 1923 | Th_SetResultDouble(interp, fLeft/fRight); |
| 1924 | break; |
| 1925 | case OP_ADD: Th_SetResultDouble(interp, fLeft+fRight); break; |
| 1926 | case OP_SUBTRACT: Th_SetResultDouble(interp, fLeft-fRight); break; |
| 1927 | case OP_LT: Th_SetResultInt(interp, fLeft<fRight); break; |
| 1928 | case OP_GT: Th_SetResultInt(interp, fLeft>fRight); break; |
| 1929 | case OP_LE: Th_SetResultInt(interp, fLeft<=fRight); break; |
| 1930 |
+115
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -827,10 +827,124 @@ | ||
| 827 | 827 | rc = TH_ERROR; |
| 828 | 828 | } |
| 829 | 829 | re_free(pRe); |
| 830 | 830 | return rc; |
| 831 | 831 | } |
| 832 | + | |
| 833 | +/* | |
| 834 | +** TH command: http ?-asynchronous? ?--? url ?payload? | |
| 835 | +** | |
| 836 | +** Perform an HTTP or HTTPS request for the specified URL. If a | |
| 837 | +** payload is present, it will be interpreted as text/plain and | |
| 838 | +** the POST method will be used; otherwise, the GET method will | |
| 839 | +** be used. Upon success, if the -asynchronous option is used, an | |
| 840 | +** empty string is returned as the result; otherwise, the response | |
| 841 | +** from the server is returned as the result. Synchronous requests | |
| 842 | +** are not currently implemented. | |
| 843 | +*/ | |
| 844 | +#define HTTP_WRONGNUMARGS "http ?-asynchronous? ?--? url ?payload?" | |
| 845 | +static int httpCmd( | |
| 846 | + Th_Interp *interp, | |
| 847 | + void *p, | |
| 848 | + int argc, | |
| 849 | + const char **argv, | |
| 850 | + int *argl | |
| 851 | +){ | |
| 852 | + int nArg = 1; | |
| 853 | + int fAsynchronous = 0; | |
| 854 | + const char *zType, *zRegexp; | |
| 855 | + Blob payload; | |
| 856 | + ReCompiled *pRe = 0; | |
| 857 | + UrlData urlData; | |
| 858 | + | |
| 859 | + if( argc<2 || argc>5 ){ | |
| 860 | + return Th_WrongNumArgs(interp, HTTP_WRONGNUMARGS); | |
| 861 | + } | |
| 862 | + if( fossil_strnicmp(argv[nArg], "-asynchronous", argl[nArg])==0 ){ | |
| 863 | + fAsynchronous = 1; nArg++; | |
| 864 | + } | |
| 865 | + if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++; | |
| 866 | + if( nArg+1!=argc && nArg+2!=argc ){ | |
| 867 | + return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS); | |
| 868 | + } | |
| 869 | + memset(&urlData, '\0', sizeof(urlData)); | |
| 870 | + url_parse_local(argv[nArg], 0, &urlData); | |
| 871 | + if( urlData.isSsh || urlData.isFile ){ | |
| 872 | + Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0); | |
| 873 | + return TH_ERROR; | |
| 874 | + } | |
| 875 | + zRegexp = db_get("th1-uri-regexp", 0); | |
| 876 | + if( zRegexp && zRegexp[0] ){ | |
| 877 | + const char *zErr = re_compile(&pRe, zRegexp, 0); | |
| 878 | + if( zErr ){ | |
| 879 | + Th_SetResult(interp, zErr, -1); | |
| 880 | + return TH_ERROR; | |
| 881 | + } | |
| 882 | + } | |
| 883 | + if( !pRe || !re_match(pRe, (const unsigned char *)urlData.canonical, -1) ){ | |
| 884 | + Th_SetResult(interp, "url not allowed", -1); | |
| 885 | + re_free(pRe); | |
| 886 | + return TH_ERROR; | |
| 887 | + } | |
| 888 | + re_free(pRe); | |
| 889 | + blob_zero(&payload); | |
| 890 | + if( nArg+2==argc ){ | |
| 891 | + blob_append(&payload, argv[nArg+1], argl[nArg+1]); | |
| 892 | + zType = "POST"; | |
| 893 | + }else{ | |
| 894 | + zType = "GET"; | |
| 895 | + } | |
| 896 | + if( fAsynchronous ){ | |
| 897 | + const char *zSep, *zParams; | |
| 898 | + Blob hdr; | |
| 899 | + zParams = strrchr(argv[nArg], '?'); | |
| 900 | + if( strlen(urlData.path)>0 && zParams!=argv[nArg] ){ | |
| 901 | + zSep = ""; | |
| 902 | + }else{ | |
| 903 | + zSep = "/"; | |
| 904 | + } | |
| 905 | + blob_zero(&hdr); | |
| 906 | + blob_appendf(&hdr, "%s %s%s%s HTTP/1.0\r\n", | |
| 907 | + zType, zSep, urlData.path, zParams ? zParams : ""); | |
| 908 | + if( urlData.proxyAuth ){ | |
| 909 | + blob_appendf(&hdr, "Proxy-Authorization: %s\r\n", urlData.proxyAuth); | |
| 910 | + } | |
| 911 | + if( urlData.passwd && urlData.user && urlData.passwd[0]=='#' ){ | |
| 912 | + char *zCredentials = mprintf("%s:%s", urlData.user, &urlData.passwd[1]); | |
| 913 | + char *zEncoded = encode64(zCredentials, -1); | |
| 914 | + blob_appendf(&hdr, "Authorization: Basic %s\r\n", zEncoded); | |
| 915 | + fossil_free(zEncoded); | |
| 916 | + fossil_free(zCredentials); | |
| 917 | + } | |
| 918 | + blob_appendf(&hdr, "Host: %s\r\n" | |
| 919 | + "User-Agent: %s\r\n", urlData.hostname, get_user_agent()); | |
| 920 | + if( zType[0]=='P' ){ | |
| 921 | + blob_appendf(&hdr, "Content-Type: application/x-www-form-urlencoded\r\n" | |
| 922 | + "Content-Length: %d\r\n\r\n", blob_size(&payload)); | |
| 923 | + }else{ | |
| 924 | + blob_appendf(&hdr, "\r\n"); | |
| 925 | + } | |
| 926 | + if( transport_open(&urlData) ){ | |
| 927 | + Th_ErrorMessage(interp, transport_errmsg(&urlData), 0, 0); | |
| 928 | + blob_reset(&hdr); | |
| 929 | + blob_reset(&payload); | |
| 930 | + return TH_ERROR; | |
| 931 | + } | |
| 932 | + transport_send(&urlData, &hdr); | |
| 933 | + transport_send(&urlData, &payload); | |
| 934 | + blob_reset(&hdr); | |
| 935 | + blob_reset(&payload); | |
| 936 | + transport_close(&urlData); | |
| 937 | + Th_SetResult(interp, 0, 0); /* NOTE: Asynchronous, no results. */ | |
| 938 | + return TH_OK; | |
| 939 | + }else{ | |
| 940 | + Th_ErrorMessage(interp, | |
| 941 | + "synchronous requests are not yet implemented", 0, 0); | |
| 942 | + blob_reset(&payload); | |
| 943 | + return TH_ERROR; | |
| 944 | + } | |
| 945 | +} | |
| 832 | 946 | |
| 833 | 947 | /* |
| 834 | 948 | ** Make sure the interpreter has been initialized. Initialize it if |
| 835 | 949 | ** it has not been already. |
| 836 | 950 | ** |
| @@ -855,10 +969,11 @@ | ||
| 855 | 969 | {"enable_output", enableOutputCmd, 0}, |
| 856 | 970 | {"hascap", hascapCmd, 0}, |
| 857 | 971 | {"hasfeature", hasfeatureCmd, 0}, |
| 858 | 972 | {"html", putsCmd, (void*)&aFlags[0]}, |
| 859 | 973 | {"htmlize", htmlizeCmd, 0}, |
| 974 | + {"http", httpCmd, 0}, | |
| 860 | 975 | {"linecount", linecntCmd, 0}, |
| 861 | 976 | {"puts", putsCmd, (void*)&aFlags[1]}, |
| 862 | 977 | {"query", queryCmd, 0}, |
| 863 | 978 | {"randhex", randhexCmd, 0}, |
| 864 | 979 | {"regexp", regexpCmd, 0}, |
| 865 | 980 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -827,10 +827,124 @@ | |
| 827 | rc = TH_ERROR; |
| 828 | } |
| 829 | re_free(pRe); |
| 830 | return rc; |
| 831 | } |
| 832 | |
| 833 | /* |
| 834 | ** Make sure the interpreter has been initialized. Initialize it if |
| 835 | ** it has not been already. |
| 836 | ** |
| @@ -855,10 +969,11 @@ | |
| 855 | {"enable_output", enableOutputCmd, 0}, |
| 856 | {"hascap", hascapCmd, 0}, |
| 857 | {"hasfeature", hasfeatureCmd, 0}, |
| 858 | {"html", putsCmd, (void*)&aFlags[0]}, |
| 859 | {"htmlize", htmlizeCmd, 0}, |
| 860 | {"linecount", linecntCmd, 0}, |
| 861 | {"puts", putsCmd, (void*)&aFlags[1]}, |
| 862 | {"query", queryCmd, 0}, |
| 863 | {"randhex", randhexCmd, 0}, |
| 864 | {"regexp", regexpCmd, 0}, |
| 865 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -827,10 +827,124 @@ | |
| 827 | rc = TH_ERROR; |
| 828 | } |
| 829 | re_free(pRe); |
| 830 | return rc; |
| 831 | } |
| 832 | |
| 833 | /* |
| 834 | ** TH command: http ?-asynchronous? ?--? url ?payload? |
| 835 | ** |
| 836 | ** Perform an HTTP or HTTPS request for the specified URL. If a |
| 837 | ** payload is present, it will be interpreted as text/plain and |
| 838 | ** the POST method will be used; otherwise, the GET method will |
| 839 | ** be used. Upon success, if the -asynchronous option is used, an |
| 840 | ** empty string is returned as the result; otherwise, the response |
| 841 | ** from the server is returned as the result. Synchronous requests |
| 842 | ** are not currently implemented. |
| 843 | */ |
| 844 | #define HTTP_WRONGNUMARGS "http ?-asynchronous? ?--? url ?payload?" |
| 845 | static int httpCmd( |
| 846 | Th_Interp *interp, |
| 847 | void *p, |
| 848 | int argc, |
| 849 | const char **argv, |
| 850 | int *argl |
| 851 | ){ |
| 852 | int nArg = 1; |
| 853 | int fAsynchronous = 0; |
| 854 | const char *zType, *zRegexp; |
| 855 | Blob payload; |
| 856 | ReCompiled *pRe = 0; |
| 857 | UrlData urlData; |
| 858 | |
| 859 | if( argc<2 || argc>5 ){ |
| 860 | return Th_WrongNumArgs(interp, HTTP_WRONGNUMARGS); |
| 861 | } |
| 862 | if( fossil_strnicmp(argv[nArg], "-asynchronous", argl[nArg])==0 ){ |
| 863 | fAsynchronous = 1; nArg++; |
| 864 | } |
| 865 | if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++; |
| 866 | if( nArg+1!=argc && nArg+2!=argc ){ |
| 867 | return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS); |
| 868 | } |
| 869 | memset(&urlData, '\0', sizeof(urlData)); |
| 870 | url_parse_local(argv[nArg], 0, &urlData); |
| 871 | if( urlData.isSsh || urlData.isFile ){ |
| 872 | Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0); |
| 873 | return TH_ERROR; |
| 874 | } |
| 875 | zRegexp = db_get("th1-uri-regexp", 0); |
| 876 | if( zRegexp && zRegexp[0] ){ |
| 877 | const char *zErr = re_compile(&pRe, zRegexp, 0); |
| 878 | if( zErr ){ |
| 879 | Th_SetResult(interp, zErr, -1); |
| 880 | return TH_ERROR; |
| 881 | } |
| 882 | } |
| 883 | if( !pRe || !re_match(pRe, (const unsigned char *)urlData.canonical, -1) ){ |
| 884 | Th_SetResult(interp, "url not allowed", -1); |
| 885 | re_free(pRe); |
| 886 | return TH_ERROR; |
| 887 | } |
| 888 | re_free(pRe); |
| 889 | blob_zero(&payload); |
| 890 | if( nArg+2==argc ){ |
| 891 | blob_append(&payload, argv[nArg+1], argl[nArg+1]); |
| 892 | zType = "POST"; |
| 893 | }else{ |
| 894 | zType = "GET"; |
| 895 | } |
| 896 | if( fAsynchronous ){ |
| 897 | const char *zSep, *zParams; |
| 898 | Blob hdr; |
| 899 | zParams = strrchr(argv[nArg], '?'); |
| 900 | if( strlen(urlData.path)>0 && zParams!=argv[nArg] ){ |
| 901 | zSep = ""; |
| 902 | }else{ |
| 903 | zSep = "/"; |
| 904 | } |
| 905 | blob_zero(&hdr); |
| 906 | blob_appendf(&hdr, "%s %s%s%s HTTP/1.0\r\n", |
| 907 | zType, zSep, urlData.path, zParams ? zParams : ""); |
| 908 | if( urlData.proxyAuth ){ |
| 909 | blob_appendf(&hdr, "Proxy-Authorization: %s\r\n", urlData.proxyAuth); |
| 910 | } |
| 911 | if( urlData.passwd && urlData.user && urlData.passwd[0]=='#' ){ |
| 912 | char *zCredentials = mprintf("%s:%s", urlData.user, &urlData.passwd[1]); |
| 913 | char *zEncoded = encode64(zCredentials, -1); |
| 914 | blob_appendf(&hdr, "Authorization: Basic %s\r\n", zEncoded); |
| 915 | fossil_free(zEncoded); |
| 916 | fossil_free(zCredentials); |
| 917 | } |
| 918 | blob_appendf(&hdr, "Host: %s\r\n" |
| 919 | "User-Agent: %s\r\n", urlData.hostname, get_user_agent()); |
| 920 | if( zType[0]=='P' ){ |
| 921 | blob_appendf(&hdr, "Content-Type: application/x-www-form-urlencoded\r\n" |
| 922 | "Content-Length: %d\r\n\r\n", blob_size(&payload)); |
| 923 | }else{ |
| 924 | blob_appendf(&hdr, "\r\n"); |
| 925 | } |
| 926 | if( transport_open(&urlData) ){ |
| 927 | Th_ErrorMessage(interp, transport_errmsg(&urlData), 0, 0); |
| 928 | blob_reset(&hdr); |
| 929 | blob_reset(&payload); |
| 930 | return TH_ERROR; |
| 931 | } |
| 932 | transport_send(&urlData, &hdr); |
| 933 | transport_send(&urlData, &payload); |
| 934 | blob_reset(&hdr); |
| 935 | blob_reset(&payload); |
| 936 | transport_close(&urlData); |
| 937 | Th_SetResult(interp, 0, 0); /* NOTE: Asynchronous, no results. */ |
| 938 | return TH_OK; |
| 939 | }else{ |
| 940 | Th_ErrorMessage(interp, |
| 941 | "synchronous requests are not yet implemented", 0, 0); |
| 942 | blob_reset(&payload); |
| 943 | return TH_ERROR; |
| 944 | } |
| 945 | } |
| 946 | |
| 947 | /* |
| 948 | ** Make sure the interpreter has been initialized. Initialize it if |
| 949 | ** it has not been already. |
| 950 | ** |
| @@ -855,10 +969,11 @@ | |
| 969 | {"enable_output", enableOutputCmd, 0}, |
| 970 | {"hascap", hascapCmd, 0}, |
| 971 | {"hasfeature", hasfeatureCmd, 0}, |
| 972 | {"html", putsCmd, (void*)&aFlags[0]}, |
| 973 | {"htmlize", htmlizeCmd, 0}, |
| 974 | {"http", httpCmd, 0}, |
| 975 | {"linecount", linecntCmd, 0}, |
| 976 | {"puts", putsCmd, (void*)&aFlags[1]}, |
| 977 | {"query", queryCmd, 0}, |
| 978 | {"randhex", randhexCmd, 0}, |
| 979 | {"regexp", regexpCmd, 0}, |
| 980 |
+19
-3
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1111,10 +1111,11 @@ | ||
| 1111 | 1111 | if( zType[0]=='a' ){ |
| 1112 | 1112 | tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH; |
| 1113 | 1113 | }else{ |
| 1114 | 1114 | tmFlags = TIMELINE_GRAPH; |
| 1115 | 1115 | } |
| 1116 | + url_add_parameter(&url, "n", mprintf("%d", nEntry)); | |
| 1116 | 1117 | if( P("ng")!=0 || zSearch!=0 ){ |
| 1117 | 1118 | tmFlags &= ~TIMELINE_GRAPH; |
| 1118 | 1119 | url_add_parameter(&url, "ng", 0); |
| 1119 | 1120 | } |
| 1120 | 1121 | if( P("brbg")!=0 ){ |
| @@ -1231,10 +1232,23 @@ | ||
| 1231 | 1232 | } |
| 1232 | 1233 | if( d_rid==0 && useDividers ) timeline_add_dividers(0, p_rid); |
| 1233 | 1234 | } |
| 1234 | 1235 | blob_appendf(&desc, " of %z[%.10s]</a>", |
| 1235 | 1236 | href("%R/info/%s", zUuid), zUuid); |
| 1237 | + if( (tmFlags & TIMELINE_UNHIDE)==0 ){ | |
| 1238 | + if( p_rid ){ | |
| 1239 | + url_add_parameter(&url, "p", zUuid); | |
| 1240 | + } | |
| 1241 | + if( d_rid ){ | |
| 1242 | + if( p_rid ){ | |
| 1243 | + /* If both p= and d= are set, we don't have the uuid of d yet. */ | |
| 1244 | + zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid); | |
| 1245 | + } | |
| 1246 | + url_add_parameter(&url, "d", zUuid); | |
| 1247 | + } | |
| 1248 | + timeline_submenu(&url, "Unhide", "unhide", "", 0); | |
| 1249 | + } | |
| 1236 | 1250 | }else if( f_rid && g.perm.Read ){ |
| 1237 | 1251 | /* If f= is present, ignore all other parameters other than n= */ |
| 1238 | 1252 | char *zUuid; |
| 1239 | 1253 | db_multi_exec( |
| 1240 | 1254 | "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" |
| @@ -1248,17 +1262,19 @@ | ||
| 1248 | 1262 | if( useDividers ) timeline_add_dividers(0, f_rid); |
| 1249 | 1263 | blob_appendf(&desc, "Parents and children of check-in "); |
| 1250 | 1264 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", f_rid); |
| 1251 | 1265 | blob_appendf(&desc, "%z[%.10s]</a>", href("%R/info/%s", zUuid), zUuid); |
| 1252 | 1266 | tmFlags |= TIMELINE_DISJOINT; |
| 1267 | + if( (tmFlags & TIMELINE_UNHIDE)==0 ){ | |
| 1268 | + url_add_parameter(&url, "f", zUuid); | |
| 1269 | + timeline_submenu(&url, "Unhide", "unhide", "", 0); | |
| 1270 | + } | |
| 1253 | 1271 | }else{ |
| 1254 | 1272 | /* Otherwise, a timeline based on a span of time */ |
| 1255 | 1273 | int n; |
| 1256 | 1274 | const char *zEType = "timeline item"; |
| 1257 | 1275 | char *zDate; |
| 1258 | - char *zNEntry = mprintf("%d", nEntry); | |
| 1259 | - url_add_parameter(&url, "n", zNEntry); | |
| 1260 | 1276 | if( zUses ){ |
| 1261 | 1277 | blob_appendf(&sql, " AND event.objid IN usesfile "); |
| 1262 | 1278 | } |
| 1263 | 1279 | if( renameOnly ){ |
| 1264 | 1280 | blob_appendf(&sql, " AND event.objid IN rnfile "); |
| @@ -1944,11 +1960,11 @@ | ||
| 1944 | 1960 | " AND blob.rid=c.cid" |
| 1945 | 1961 | ); |
| 1946 | 1962 | while( db_step(&q)==SQLITE_ROW ){ |
| 1947 | 1963 | const char *zUuid = db_column_text(&q, 0); |
| 1948 | 1964 | @ <li> |
| 1949 | - @ <a href="%s(g.zTop)/timeline?p=%S(zUuid)&d=%S(zUuid)">%S(zUuid)</a> | |
| 1965 | + @ <a href="%s(g.zTop)/timeline?p=%S(zUuid)&d=%S(zUuid)&unhide">%S(zUuid)</a> | |
| 1950 | 1966 | } |
| 1951 | 1967 | db_finalize(&q); |
| 1952 | 1968 | style_footer(); |
| 1953 | 1969 | } |
| 1954 | 1970 | |
| 1955 | 1971 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1111,10 +1111,11 @@ | |
| 1111 | if( zType[0]=='a' ){ |
| 1112 | tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH; |
| 1113 | }else{ |
| 1114 | tmFlags = TIMELINE_GRAPH; |
| 1115 | } |
| 1116 | if( P("ng")!=0 || zSearch!=0 ){ |
| 1117 | tmFlags &= ~TIMELINE_GRAPH; |
| 1118 | url_add_parameter(&url, "ng", 0); |
| 1119 | } |
| 1120 | if( P("brbg")!=0 ){ |
| @@ -1231,10 +1232,23 @@ | |
| 1231 | } |
| 1232 | if( d_rid==0 && useDividers ) timeline_add_dividers(0, p_rid); |
| 1233 | } |
| 1234 | blob_appendf(&desc, " of %z[%.10s]</a>", |
| 1235 | href("%R/info/%s", zUuid), zUuid); |
| 1236 | }else if( f_rid && g.perm.Read ){ |
| 1237 | /* If f= is present, ignore all other parameters other than n= */ |
| 1238 | char *zUuid; |
| 1239 | db_multi_exec( |
| 1240 | "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" |
| @@ -1248,17 +1262,19 @@ | |
| 1248 | if( useDividers ) timeline_add_dividers(0, f_rid); |
| 1249 | blob_appendf(&desc, "Parents and children of check-in "); |
| 1250 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", f_rid); |
| 1251 | blob_appendf(&desc, "%z[%.10s]</a>", href("%R/info/%s", zUuid), zUuid); |
| 1252 | tmFlags |= TIMELINE_DISJOINT; |
| 1253 | }else{ |
| 1254 | /* Otherwise, a timeline based on a span of time */ |
| 1255 | int n; |
| 1256 | const char *zEType = "timeline item"; |
| 1257 | char *zDate; |
| 1258 | char *zNEntry = mprintf("%d", nEntry); |
| 1259 | url_add_parameter(&url, "n", zNEntry); |
| 1260 | if( zUses ){ |
| 1261 | blob_appendf(&sql, " AND event.objid IN usesfile "); |
| 1262 | } |
| 1263 | if( renameOnly ){ |
| 1264 | blob_appendf(&sql, " AND event.objid IN rnfile "); |
| @@ -1944,11 +1960,11 @@ | |
| 1944 | " AND blob.rid=c.cid" |
| 1945 | ); |
| 1946 | while( db_step(&q)==SQLITE_ROW ){ |
| 1947 | const char *zUuid = db_column_text(&q, 0); |
| 1948 | @ <li> |
| 1949 | @ <a href="%s(g.zTop)/timeline?p=%S(zUuid)&d=%S(zUuid)">%S(zUuid)</a> |
| 1950 | } |
| 1951 | db_finalize(&q); |
| 1952 | style_footer(); |
| 1953 | } |
| 1954 | |
| 1955 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1111,10 +1111,11 @@ | |
| 1111 | if( zType[0]=='a' ){ |
| 1112 | tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH; |
| 1113 | }else{ |
| 1114 | tmFlags = TIMELINE_GRAPH; |
| 1115 | } |
| 1116 | url_add_parameter(&url, "n", mprintf("%d", nEntry)); |
| 1117 | if( P("ng")!=0 || zSearch!=0 ){ |
| 1118 | tmFlags &= ~TIMELINE_GRAPH; |
| 1119 | url_add_parameter(&url, "ng", 0); |
| 1120 | } |
| 1121 | if( P("brbg")!=0 ){ |
| @@ -1231,10 +1232,23 @@ | |
| 1232 | } |
| 1233 | if( d_rid==0 && useDividers ) timeline_add_dividers(0, p_rid); |
| 1234 | } |
| 1235 | blob_appendf(&desc, " of %z[%.10s]</a>", |
| 1236 | href("%R/info/%s", zUuid), zUuid); |
| 1237 | if( (tmFlags & TIMELINE_UNHIDE)==0 ){ |
| 1238 | if( p_rid ){ |
| 1239 | url_add_parameter(&url, "p", zUuid); |
| 1240 | } |
| 1241 | if( d_rid ){ |
| 1242 | if( p_rid ){ |
| 1243 | /* If both p= and d= are set, we don't have the uuid of d yet. */ |
| 1244 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid); |
| 1245 | } |
| 1246 | url_add_parameter(&url, "d", zUuid); |
| 1247 | } |
| 1248 | timeline_submenu(&url, "Unhide", "unhide", "", 0); |
| 1249 | } |
| 1250 | }else if( f_rid && g.perm.Read ){ |
| 1251 | /* If f= is present, ignore all other parameters other than n= */ |
| 1252 | char *zUuid; |
| 1253 | db_multi_exec( |
| 1254 | "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" |
| @@ -1248,17 +1262,19 @@ | |
| 1262 | if( useDividers ) timeline_add_dividers(0, f_rid); |
| 1263 | blob_appendf(&desc, "Parents and children of check-in "); |
| 1264 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", f_rid); |
| 1265 | blob_appendf(&desc, "%z[%.10s]</a>", href("%R/info/%s", zUuid), zUuid); |
| 1266 | tmFlags |= TIMELINE_DISJOINT; |
| 1267 | if( (tmFlags & TIMELINE_UNHIDE)==0 ){ |
| 1268 | url_add_parameter(&url, "f", zUuid); |
| 1269 | timeline_submenu(&url, "Unhide", "unhide", "", 0); |
| 1270 | } |
| 1271 | }else{ |
| 1272 | /* Otherwise, a timeline based on a span of time */ |
| 1273 | int n; |
| 1274 | const char *zEType = "timeline item"; |
| 1275 | char *zDate; |
| 1276 | if( zUses ){ |
| 1277 | blob_appendf(&sql, " AND event.objid IN usesfile "); |
| 1278 | } |
| 1279 | if( renameOnly ){ |
| 1280 | blob_appendf(&sql, " AND event.objid IN rnfile "); |
| @@ -1944,11 +1960,11 @@ | |
| 1960 | " AND blob.rid=c.cid" |
| 1961 | ); |
| 1962 | while( db_step(&q)==SQLITE_ROW ){ |
| 1963 | const char *zUuid = db_column_text(&q, 0); |
| 1964 | @ <li> |
| 1965 | @ <a href="%s(g.zTop)/timeline?p=%S(zUuid)&d=%S(zUuid)&unhide">%S(zUuid)</a> |
| 1966 | } |
| 1967 | db_finalize(&q); |
| 1968 | style_footer(); |
| 1969 | } |
| 1970 | |
| 1971 |
+9
-4
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -513,15 +513,16 @@ | ||
| 513 | 513 | } |
| 514 | 514 | |
| 515 | 515 | /* |
| 516 | 516 | ** Write a ticket into the repository. |
| 517 | 517 | */ |
| 518 | -static void ticket_put( | |
| 518 | +static int ticket_put( | |
| 519 | 519 | Blob *pTicket, /* The text of the ticket change record */ |
| 520 | 520 | const char *zTktId, /* The ticket to which this change is applied */ |
| 521 | 521 | int needMod /* True if moderation is needed */ |
| 522 | 522 | ){ |
| 523 | + int result; | |
| 523 | 524 | int rid = content_put_ex(pTicket, 0, 0, 0, needMod); |
| 524 | 525 | if( rid==0 ){ |
| 525 | 526 | fossil_fatal("trouble committing ticket: %s", g.zErrMsg); |
| 526 | 527 | } |
| 527 | 528 | if( needMod ){ |
| @@ -533,13 +534,14 @@ | ||
| 533 | 534 | }else{ |
| 534 | 535 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); |
| 535 | 536 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); |
| 536 | 537 | } |
| 537 | 538 | manifest_crosslink_begin(); |
| 538 | - manifest_crosslink(rid, pTicket); | |
| 539 | + result = (manifest_crosslink(rid, pTicket, MC_PERMIT_HOOKS)==0); | |
| 539 | 540 | assert( blob_is_reset(pTicket) ); |
| 540 | 541 | manifest_crosslink_end(); |
| 542 | + return result; | |
| 541 | 543 | } |
| 542 | 544 | |
| 543 | 545 | /* |
| 544 | 546 | ** Subscript command: submit_ticket |
| 545 | 547 | ** |
| @@ -1344,11 +1346,14 @@ | ||
| 1344 | 1346 | } |
| 1345 | 1347 | blob_appendf(&tktchng, "K %s\n", zTktUuid); |
| 1346 | 1348 | blob_appendf(&tktchng, "U %F\n", zUser); |
| 1347 | 1349 | md5sum_blob(&tktchng, &cksum); |
| 1348 | 1350 | blob_appendf(&tktchng, "Z %b\n", &cksum); |
| 1349 | - ticket_put(&tktchng, zTktUuid, 0); | |
| 1350 | - printf("ticket %s succeeded for %s\n", | |
| 1351 | + if( ticket_put(&tktchng, zTktUuid, 0) ){ | |
| 1352 | + fossil_fatal("%s\n", g.zErrMsg); | |
| 1353 | + }else{ | |
| 1354 | + fossil_print("ticket %s succeeded for %s\n", | |
| 1351 | 1355 | (eCmd==set?"set":"add"),zTktUuid); |
| 1356 | + } | |
| 1352 | 1357 | } |
| 1353 | 1358 | } |
| 1354 | 1359 | } |
| 1355 | 1360 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -513,15 +513,16 @@ | |
| 513 | } |
| 514 | |
| 515 | /* |
| 516 | ** Write a ticket into the repository. |
| 517 | */ |
| 518 | static void ticket_put( |
| 519 | Blob *pTicket, /* The text of the ticket change record */ |
| 520 | const char *zTktId, /* The ticket to which this change is applied */ |
| 521 | int needMod /* True if moderation is needed */ |
| 522 | ){ |
| 523 | int rid = content_put_ex(pTicket, 0, 0, 0, needMod); |
| 524 | if( rid==0 ){ |
| 525 | fossil_fatal("trouble committing ticket: %s", g.zErrMsg); |
| 526 | } |
| 527 | if( needMod ){ |
| @@ -533,13 +534,14 @@ | |
| 533 | }else{ |
| 534 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); |
| 535 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); |
| 536 | } |
| 537 | manifest_crosslink_begin(); |
| 538 | manifest_crosslink(rid, pTicket); |
| 539 | assert( blob_is_reset(pTicket) ); |
| 540 | manifest_crosslink_end(); |
| 541 | } |
| 542 | |
| 543 | /* |
| 544 | ** Subscript command: submit_ticket |
| 545 | ** |
| @@ -1344,11 +1346,14 @@ | |
| 1344 | } |
| 1345 | blob_appendf(&tktchng, "K %s\n", zTktUuid); |
| 1346 | blob_appendf(&tktchng, "U %F\n", zUser); |
| 1347 | md5sum_blob(&tktchng, &cksum); |
| 1348 | blob_appendf(&tktchng, "Z %b\n", &cksum); |
| 1349 | ticket_put(&tktchng, zTktUuid, 0); |
| 1350 | printf("ticket %s succeeded for %s\n", |
| 1351 | (eCmd==set?"set":"add"),zTktUuid); |
| 1352 | } |
| 1353 | } |
| 1354 | } |
| 1355 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -513,15 +513,16 @@ | |
| 513 | } |
| 514 | |
| 515 | /* |
| 516 | ** Write a ticket into the repository. |
| 517 | */ |
| 518 | static int ticket_put( |
| 519 | Blob *pTicket, /* The text of the ticket change record */ |
| 520 | const char *zTktId, /* The ticket to which this change is applied */ |
| 521 | int needMod /* True if moderation is needed */ |
| 522 | ){ |
| 523 | int result; |
| 524 | int rid = content_put_ex(pTicket, 0, 0, 0, needMod); |
| 525 | if( rid==0 ){ |
| 526 | fossil_fatal("trouble committing ticket: %s", g.zErrMsg); |
| 527 | } |
| 528 | if( needMod ){ |
| @@ -533,13 +534,14 @@ | |
| 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 |
| 547 | ** |
| @@ -1344,11 +1346,14 @@ | |
| 1346 | } |
| 1347 | blob_appendf(&tktchng, "K %s\n", zTktUuid); |
| 1348 | blob_appendf(&tktchng, "U %F\n", zUser); |
| 1349 | md5sum_blob(&tktchng, &cksum); |
| 1350 | blob_appendf(&tktchng, "Z %b\n", &cksum); |
| 1351 | if( ticket_put(&tktchng, zTktUuid, 0) ){ |
| 1352 | fossil_fatal("%s\n", g.zErrMsg); |
| 1353 | }else{ |
| 1354 | fossil_print("ticket %s succeeded for %s\n", |
| 1355 | (eCmd==set?"set":"add"),zTktUuid); |
| 1356 | } |
| 1357 | } |
| 1358 | } |
| 1359 | } |
| 1360 |
+244
-184
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -39,10 +39,34 @@ | ||
| 39 | 39 | #define URL_REMEMBER 0x002 /* Remember the url for later reuse */ |
| 40 | 40 | #define URL_ASK_REMEMBER_PW 0x004 /* Ask whether to remember prompted pw */ |
| 41 | 41 | #define URL_REMEMBER_PW 0x008 /* Should remember pw */ |
| 42 | 42 | #define URL_PROMPTED 0x010 /* Prompted for PW already */ |
| 43 | 43 | |
| 44 | +/* | |
| 45 | +** The URL related data used with this subsystem. | |
| 46 | +*/ | |
| 47 | +struct UrlData { | |
| 48 | + /* | |
| 49 | + ** NOTE: These members MUST be kept in sync with the related ones in the | |
| 50 | + ** "Global" structure defined in "main.c". | |
| 51 | + */ | |
| 52 | + int isFile; /* True if a "file:" url */ | |
| 53 | + int isHttps; /* True if a "https:" url */ | |
| 54 | + int isSsh; /* True if an "ssh:" url */ | |
| 55 | + char *name; /* Hostname for http: or filename for file: */ | |
| 56 | + char *hostname; /* The HOST: parameter on http headers */ | |
| 57 | + char *protocol; /* "http" or "https" */ | |
| 58 | + int port; /* TCP port number for http: or https: */ | |
| 59 | + int dfltPort; /* The default port for the given protocol */ | |
| 60 | + char *path; /* Pathname for http: */ | |
| 61 | + char *user; /* User id for http: */ | |
| 62 | + char *passwd; /* Password for http: */ | |
| 63 | + char *canonical; /* Canonical representation of the URL */ | |
| 64 | + char *proxyAuth; /* Proxy-Authorizer: string */ | |
| 65 | + char *fossil; /* The fossil query parameter on ssh: */ | |
| 66 | + unsigned flags; /* Boolean flags controlling URL processing */ | |
| 67 | +}; | |
| 44 | 68 | #endif /* INTERFACE */ |
| 45 | 69 | |
| 46 | 70 | |
| 47 | 71 | /* |
| 48 | 72 | ** Convert a string to lower-case. |
| @@ -51,10 +75,201 @@ | ||
| 51 | 75 | while( *z ){ |
| 52 | 76 | *z = fossil_tolower(*z); |
| 53 | 77 | z++; |
| 54 | 78 | } |
| 55 | 79 | } |
| 80 | + | |
| 81 | +/* | |
| 82 | +** Parse the given URL. Populate members of the provided UrlData structure | |
| 83 | +** as follows: | |
| 84 | +** | |
| 85 | +** isFile True if FILE: | |
| 86 | +** isHttps True if HTTPS: | |
| 87 | +** isSsh True if SSH: | |
| 88 | +** protocol "http" or "https" or "file" | |
| 89 | +** name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: | |
| 90 | +** port TCP port number for HTTP or HTTPS. | |
| 91 | +** dfltPort Default TCP port number (80 or 443). | |
| 92 | +** path Path name for HTTP or HTTPS. | |
| 93 | +** user Userid. | |
| 94 | +** passwd Password. | |
| 95 | +** hostname HOST:PORT or just HOST if port is the default. | |
| 96 | +** canonical The URL in canonical form, omitting the password | |
| 97 | +** | |
| 98 | +*/ | |
| 99 | +void url_parse_local( | |
| 100 | + const char *zUrl, | |
| 101 | + unsigned int urlFlags, | |
| 102 | + UrlData *pUrlData | |
| 103 | +){ | |
| 104 | + int i, j, c; | |
| 105 | + char *zFile = 0; | |
| 106 | + | |
| 107 | + if( zUrl==0 ){ | |
| 108 | + zUrl = db_get("last-sync-url", 0); | |
| 109 | + if( zUrl==0 ) return; | |
| 110 | + if( pUrlData->passwd==0 ){ | |
| 111 | + pUrlData->passwd = unobscure(db_get("last-sync-pw", 0)); | |
| 112 | + } | |
| 113 | + } | |
| 114 | + | |
| 115 | + if( strncmp(zUrl, "http://", 7)==0 | |
| 116 | + || strncmp(zUrl, "https://", 8)==0 | |
| 117 | + || strncmp(zUrl, "ssh://", 6)==0 | |
| 118 | + ){ | |
| 119 | + int iStart; | |
| 120 | + char *zLogin; | |
| 121 | + char *zExe; | |
| 122 | + char cQuerySep = '?'; | |
| 123 | + | |
| 124 | + pUrlData->isFile = 0; | |
| 125 | + if( zUrl[4]=='s' ){ | |
| 126 | + pUrlData->isHttps = 1; | |
| 127 | + pUrlData->protocol = "https"; | |
| 128 | + pUrlData->dfltPort = 443; | |
| 129 | + iStart = 8; | |
| 130 | + }else if( zUrl[0]=='s' ){ | |
| 131 | + pUrlData->isSsh = 1; | |
| 132 | + pUrlData->protocol = "ssh"; | |
| 133 | + pUrlData->dfltPort = 22; | |
| 134 | + pUrlData->fossil = "fossil"; | |
| 135 | + iStart = 6; | |
| 136 | + }else{ | |
| 137 | + pUrlData->isHttps = 0; | |
| 138 | + pUrlData->protocol = "http"; | |
| 139 | + pUrlData->dfltPort = 80; | |
| 140 | + iStart = 7; | |
| 141 | + } | |
| 142 | + for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} | |
| 143 | + if( c=='@' ){ | |
| 144 | + /* Parse up the user-id and password */ | |
| 145 | + for(j=iStart; j<i && zUrl[j]!=':'; j++){} | |
| 146 | + pUrlData->user = mprintf("%.*s", j-iStart, &zUrl[iStart]); | |
| 147 | + dehttpize(pUrlData->user); | |
| 148 | + if( j<i ){ | |
| 149 | + if( ( urlFlags & URL_REMEMBER ) && pUrlData->isSsh==0 ){ | |
| 150 | + urlFlags |= URL_ASK_REMEMBER_PW; | |
| 151 | + } | |
| 152 | + pUrlData->passwd = mprintf("%.*s", i-j-1, &zUrl[j+1]); | |
| 153 | + dehttpize(pUrlData->passwd); | |
| 154 | + } | |
| 155 | + if( pUrlData->isSsh ){ | |
| 156 | + urlFlags &= ~URL_ASK_REMEMBER_PW; | |
| 157 | + } | |
| 158 | + zLogin = mprintf("%t@", pUrlData->user); | |
| 159 | + for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} | |
| 160 | + pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]); | |
| 161 | + i = j; | |
| 162 | + }else{ | |
| 163 | + for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){} | |
| 164 | + pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]); | |
| 165 | + zLogin = mprintf(""); | |
| 166 | + } | |
| 167 | + url_tolower(pUrlData->name); | |
| 168 | + if( c==':' ){ | |
| 169 | + pUrlData->port = 0; | |
| 170 | + i++; | |
| 171 | + while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ | |
| 172 | + pUrlData->port = pUrlData->port*10 + c - '0'; | |
| 173 | + i++; | |
| 174 | + } | |
| 175 | + pUrlData->hostname = mprintf("%s:%d", pUrlData->name, pUrlData->port); | |
| 176 | + }else{ | |
| 177 | + pUrlData->port = pUrlData->dfltPort; | |
| 178 | + pUrlData->hostname = pUrlData->name; | |
| 179 | + } | |
| 180 | + dehttpize(pUrlData->name); | |
| 181 | + pUrlData->path = mprintf("%s", &zUrl[i]); | |
| 182 | + for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){} | |
| 183 | + if( pUrlData->path[i] ){ | |
| 184 | + pUrlData->path[i] = 0; | |
| 185 | + i++; | |
| 186 | + } | |
| 187 | + zExe = mprintf(""); | |
| 188 | + while( pUrlData->path[i]!=0 ){ | |
| 189 | + char *zName, *zValue; | |
| 190 | + zName = &pUrlData->path[i]; | |
| 191 | + zValue = zName; | |
| 192 | + while( pUrlData->path[i] && pUrlData->path[i]!='=' ){ i++; } | |
| 193 | + if( pUrlData->path[i]=='=' ){ | |
| 194 | + pUrlData->path[i] = 0; | |
| 195 | + i++; | |
| 196 | + zValue = &pUrlData->path[i]; | |
| 197 | + while( pUrlData->path[i] && pUrlData->path[i]!='&' ){ i++; } | |
| 198 | + } | |
| 199 | + if( pUrlData->path[i] ){ | |
| 200 | + pUrlData->path[i] = 0; | |
| 201 | + i++; | |
| 202 | + } | |
| 203 | + if( fossil_strcmp(zName,"fossil")==0 ){ | |
| 204 | + pUrlData->fossil = zValue; | |
| 205 | + dehttpize(pUrlData->fossil); | |
| 206 | + zExe = mprintf("%cfossil=%T", cQuerySep, pUrlData->fossil); | |
| 207 | + cQuerySep = '&'; | |
| 208 | + } | |
| 209 | + } | |
| 210 | + | |
| 211 | + dehttpize(pUrlData->path); | |
| 212 | + if( pUrlData->dfltPort==pUrlData->port ){ | |
| 213 | + pUrlData->canonical = mprintf( | |
| 214 | + "%s://%s%T%T%s", | |
| 215 | + pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe | |
| 216 | + ); | |
| 217 | + }else{ | |
| 218 | + pUrlData->canonical = mprintf( | |
| 219 | + "%s://%s%T:%d%T%s", | |
| 220 | + pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port, | |
| 221 | + pUrlData->path, zExe | |
| 222 | + ); | |
| 223 | + } | |
| 224 | + if( pUrlData->isSsh && pUrlData->path[1] ) pUrlData->path++; | |
| 225 | + free(zLogin); | |
| 226 | + }else if( strncmp(zUrl, "file:", 5)==0 ){ | |
| 227 | + pUrlData->isFile = 1; | |
| 228 | + if( zUrl[5]=='/' && zUrl[6]=='/' ){ | |
| 229 | + i = 7; | |
| 230 | + }else{ | |
| 231 | + i = 5; | |
| 232 | + } | |
| 233 | + zFile = mprintf("%s", &zUrl[i]); | |
| 234 | + }else if( file_isfile(zUrl) ){ | |
| 235 | + pUrlData->isFile = 1; | |
| 236 | + zFile = mprintf("%s", zUrl); | |
| 237 | + }else if( file_isdir(zUrl)==1 ){ | |
| 238 | + zFile = mprintf("%s/FOSSIL", zUrl); | |
| 239 | + if( file_isfile(zFile) ){ | |
| 240 | + pUrlData->isFile = 1; | |
| 241 | + }else{ | |
| 242 | + free(zFile); | |
| 243 | + fossil_fatal("unknown repository: %s", zUrl); | |
| 244 | + } | |
| 245 | + }else{ | |
| 246 | + fossil_fatal("unknown repository: %s", zUrl); | |
| 247 | + } | |
| 248 | + if( urlFlags ) pUrlData->flags = urlFlags; | |
| 249 | + if( pUrlData->isFile ){ | |
| 250 | + Blob cfile; | |
| 251 | + dehttpize(zFile); | |
| 252 | + file_canonical_name(zFile, &cfile, 0); | |
| 253 | + free(zFile); | |
| 254 | + pUrlData->protocol = "file"; | |
| 255 | + pUrlData->path = ""; | |
| 256 | + pUrlData->name = mprintf("%b", &cfile); | |
| 257 | + pUrlData->canonical = mprintf("file://%T", pUrlData->name); | |
| 258 | + blob_reset(&cfile); | |
| 259 | + }else if( pUrlData->user!=0 && pUrlData->passwd==0 && (urlFlags & URL_PROMPT_PW) ){ | |
| 260 | + url_prompt_for_password_local(pUrlData); | |
| 261 | + }else if( pUrlData->user!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){ | |
| 262 | + if( isatty(fileno(stdin)) ){ | |
| 263 | + if( save_password_prompt(pUrlData->passwd) ){ | |
| 264 | + pUrlData->flags = urlFlags |= URL_REMEMBER_PW; | |
| 265 | + }else{ | |
| 266 | + pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW; | |
| 267 | + } | |
| 268 | + } | |
| 269 | + } | |
| 270 | +} | |
| 56 | 271 | |
| 57 | 272 | /* |
| 58 | 273 | ** Parse the given URL, which describes a sync server. Populate variables |
| 59 | 274 | ** in the global "g" structure as follows: |
| 60 | 275 | ** |
| @@ -79,175 +294,11 @@ | ||
| 79 | 294 | ** |
| 80 | 295 | ** ssh://userid@host:port/path?fossil=path/to/fossil.exe |
| 81 | 296 | ** |
| 82 | 297 | */ |
| 83 | 298 | void url_parse(const char *zUrl, unsigned int urlFlags){ |
| 84 | - int i, j, c; | |
| 85 | - char *zFile = 0; | |
| 86 | - | |
| 87 | - if( zUrl==0 ){ | |
| 88 | - zUrl = db_get("last-sync-url", 0); | |
| 89 | - if( zUrl==0 ) return; | |
| 90 | - if( g.urlPasswd==0 ){ | |
| 91 | - g.urlPasswd = unobscure(db_get("last-sync-pw", 0)); | |
| 92 | - } | |
| 93 | - } | |
| 94 | - | |
| 95 | - if( strncmp(zUrl, "http://", 7)==0 | |
| 96 | - || strncmp(zUrl, "https://", 8)==0 | |
| 97 | - || strncmp(zUrl, "ssh://", 6)==0 | |
| 98 | - ){ | |
| 99 | - int iStart; | |
| 100 | - char *zLogin; | |
| 101 | - char *zExe; | |
| 102 | - char cQuerySep = '?'; | |
| 103 | - | |
| 104 | - g.urlIsFile = 0; | |
| 105 | - if( zUrl[4]=='s' ){ | |
| 106 | - g.urlIsHttps = 1; | |
| 107 | - g.urlProtocol = "https"; | |
| 108 | - g.urlDfltPort = 443; | |
| 109 | - iStart = 8; | |
| 110 | - }else if( zUrl[0]=='s' ){ | |
| 111 | - g.urlIsSsh = 1; | |
| 112 | - g.urlProtocol = "ssh"; | |
| 113 | - g.urlDfltPort = 22; | |
| 114 | - g.urlFossil = "fossil"; | |
| 115 | - iStart = 6; | |
| 116 | - }else{ | |
| 117 | - g.urlIsHttps = 0; | |
| 118 | - g.urlProtocol = "http"; | |
| 119 | - g.urlDfltPort = 80; | |
| 120 | - iStart = 7; | |
| 121 | - } | |
| 122 | - for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} | |
| 123 | - if( c=='@' ){ | |
| 124 | - /* Parse up the user-id and password */ | |
| 125 | - for(j=iStart; j<i && zUrl[j]!=':'; j++){} | |
| 126 | - g.urlUser = mprintf("%.*s", j-iStart, &zUrl[iStart]); | |
| 127 | - dehttpize(g.urlUser); | |
| 128 | - if( j<i ){ | |
| 129 | - if( ( urlFlags & URL_REMEMBER ) && g.urlIsSsh==0 ){ | |
| 130 | - urlFlags |= URL_ASK_REMEMBER_PW; | |
| 131 | - } | |
| 132 | - g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]); | |
| 133 | - dehttpize(g.urlPasswd); | |
| 134 | - } | |
| 135 | - if( g.urlIsSsh ){ | |
| 136 | - urlFlags &= ~URL_ASK_REMEMBER_PW; | |
| 137 | - } | |
| 138 | - zLogin = mprintf("%t@", g.urlUser); | |
| 139 | - for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} | |
| 140 | - g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]); | |
| 141 | - i = j; | |
| 142 | - }else{ | |
| 143 | - for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){} | |
| 144 | - g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]); | |
| 145 | - zLogin = mprintf(""); | |
| 146 | - } | |
| 147 | - url_tolower(g.urlName); | |
| 148 | - if( c==':' ){ | |
| 149 | - g.urlPort = 0; | |
| 150 | - i++; | |
| 151 | - while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ | |
| 152 | - g.urlPort = g.urlPort*10 + c - '0'; | |
| 153 | - i++; | |
| 154 | - } | |
| 155 | - g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort); | |
| 156 | - }else{ | |
| 157 | - g.urlPort = g.urlDfltPort; | |
| 158 | - g.urlHostname = g.urlName; | |
| 159 | - } | |
| 160 | - dehttpize(g.urlName); | |
| 161 | - g.urlPath = mprintf("%s", &zUrl[i]); | |
| 162 | - for(i=0; g.urlPath[i] && g.urlPath[i]!='?'; i++){} | |
| 163 | - if( g.urlPath[i] ){ | |
| 164 | - g.urlPath[i] = 0; | |
| 165 | - i++; | |
| 166 | - } | |
| 167 | - zExe = mprintf(""); | |
| 168 | - while( g.urlPath[i]!=0 ){ | |
| 169 | - char *zName, *zValue; | |
| 170 | - zName = &g.urlPath[i]; | |
| 171 | - zValue = zName; | |
| 172 | - while( g.urlPath[i] && g.urlPath[i]!='=' ){ i++; } | |
| 173 | - if( g.urlPath[i]=='=' ){ | |
| 174 | - g.urlPath[i] = 0; | |
| 175 | - i++; | |
| 176 | - zValue = &g.urlPath[i]; | |
| 177 | - while( g.urlPath[i] && g.urlPath[i]!='&' ){ i++; } | |
| 178 | - } | |
| 179 | - if( g.urlPath[i] ){ | |
| 180 | - g.urlPath[i] = 0; | |
| 181 | - i++; | |
| 182 | - } | |
| 183 | - if( fossil_strcmp(zName,"fossil")==0 ){ | |
| 184 | - g.urlFossil = zValue; | |
| 185 | - dehttpize(g.urlFossil); | |
| 186 | - zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil); | |
| 187 | - cQuerySep = '&'; | |
| 188 | - } | |
| 189 | - } | |
| 190 | - | |
| 191 | - dehttpize(g.urlPath); | |
| 192 | - if( g.urlDfltPort==g.urlPort ){ | |
| 193 | - g.urlCanonical = mprintf( | |
| 194 | - "%s://%s%T%T%s", | |
| 195 | - g.urlProtocol, zLogin, g.urlName, g.urlPath, zExe | |
| 196 | - ); | |
| 197 | - }else{ | |
| 198 | - g.urlCanonical = mprintf( | |
| 199 | - "%s://%s%T:%d%T%s", | |
| 200 | - g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath, zExe | |
| 201 | - ); | |
| 202 | - } | |
| 203 | - if( g.urlIsSsh && g.urlPath[1] ) g.urlPath++; | |
| 204 | - free(zLogin); | |
| 205 | - }else if( strncmp(zUrl, "file:", 5)==0 ){ | |
| 206 | - g.urlIsFile = 1; | |
| 207 | - if( zUrl[5]=='/' && zUrl[6]=='/' ){ | |
| 208 | - i = 7; | |
| 209 | - }else{ | |
| 210 | - i = 5; | |
| 211 | - } | |
| 212 | - zFile = mprintf("%s", &zUrl[i]); | |
| 213 | - }else if( file_isfile(zUrl) ){ | |
| 214 | - g.urlIsFile = 1; | |
| 215 | - zFile = mprintf("%s", zUrl); | |
| 216 | - }else if( file_isdir(zUrl)==1 ){ | |
| 217 | - zFile = mprintf("%s/FOSSIL", zUrl); | |
| 218 | - if( file_isfile(zFile) ){ | |
| 219 | - g.urlIsFile = 1; | |
| 220 | - }else{ | |
| 221 | - free(zFile); | |
| 222 | - fossil_fatal("unknown repository: %s", zUrl); | |
| 223 | - } | |
| 224 | - }else{ | |
| 225 | - fossil_fatal("unknown repository: %s", zUrl); | |
| 226 | - } | |
| 227 | - if( urlFlags ) g.urlFlags = urlFlags; | |
| 228 | - if( g.urlIsFile ){ | |
| 229 | - Blob cfile; | |
| 230 | - dehttpize(zFile); | |
| 231 | - file_canonical_name(zFile, &cfile, 0); | |
| 232 | - free(zFile); | |
| 233 | - g.urlProtocol = "file"; | |
| 234 | - g.urlPath = ""; | |
| 235 | - g.urlName = mprintf("%b", &cfile); | |
| 236 | - g.urlCanonical = mprintf("file://%T", g.urlName); | |
| 237 | - blob_reset(&cfile); | |
| 238 | - }else if( g.urlUser!=0 && g.urlPasswd==0 && (urlFlags & URL_PROMPT_PW) ){ | |
| 239 | - url_prompt_for_password(); | |
| 240 | - }else if( g.urlUser!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){ | |
| 241 | - if( isatty(fileno(stdin)) ){ | |
| 242 | - if( save_password_prompt() ){ | |
| 243 | - g.urlFlags = urlFlags |= URL_REMEMBER_PW; | |
| 244 | - }else{ | |
| 245 | - g.urlFlags = urlFlags &= ~URL_REMEMBER_PW; | |
| 246 | - } | |
| 247 | - } | |
| 248 | - } | |
| 299 | + url_parse_local(zUrl, urlFlags, GLOBAL_URL()); | |
| 249 | 300 | } |
| 250 | 301 | |
| 251 | 302 | /* |
| 252 | 303 | ** COMMAND: test-urlparser |
| 253 | 304 | ** |
| @@ -439,35 +490,44 @@ | ||
| 439 | 490 | } |
| 440 | 491 | return blob_str(&p->url); |
| 441 | 492 | } |
| 442 | 493 | |
| 443 | 494 | /* |
| 444 | -** Prompt the user for the password for g.urlUser. Store the result | |
| 445 | -** in g.urlPasswd. | |
| 446 | -*/ | |
| 447 | -void url_prompt_for_password(void){ | |
| 448 | - if( g.urlIsSsh || g.urlIsFile ) return; | |
| 449 | - if( isatty(fileno(stdin)) | |
| 450 | - && (g.urlFlags & URL_PROMPT_PW)!=0 | |
| 451 | - && (g.urlFlags & URL_PROMPTED)==0 | |
| 452 | - ){ | |
| 453 | - g.urlFlags |= URL_PROMPTED; | |
| 454 | - g.urlPasswd = prompt_for_user_password(g.urlUser); | |
| 455 | - if( g.urlPasswd[0] | |
| 456 | - && (g.urlFlags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 | |
| 457 | - ){ | |
| 458 | - if( save_password_prompt() ){ | |
| 459 | - g.urlFlags |= URL_REMEMBER_PW; | |
| 460 | - }else{ | |
| 461 | - g.urlFlags &= ~URL_REMEMBER_PW; | |
| 495 | +** Prompt the user for the password that corresponds to the "user" member of | |
| 496 | +** the provided UrlData structure. Store the result into the "passwd" member | |
| 497 | +** of the provided UrlData structure. | |
| 498 | +*/ | |
| 499 | +void url_prompt_for_password_local(UrlData *pUrlData){ | |
| 500 | + if( pUrlData->isSsh || pUrlData->isFile ) return; | |
| 501 | + if( isatty(fileno(stdin)) | |
| 502 | + && (pUrlData->flags & URL_PROMPT_PW)!=0 | |
| 503 | + && (pUrlData->flags & URL_PROMPTED)==0 | |
| 504 | + ){ | |
| 505 | + pUrlData->flags |= URL_PROMPTED; | |
| 506 | + pUrlData->passwd = prompt_for_user_password(pUrlData->user); | |
| 507 | + if( pUrlData->passwd[0] | |
| 508 | + && (pUrlData->flags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 | |
| 509 | + ){ | |
| 510 | + if( save_password_prompt(pUrlData->passwd) ){ | |
| 511 | + pUrlData->flags |= URL_REMEMBER_PW; | |
| 512 | + }else{ | |
| 513 | + pUrlData->flags &= ~URL_REMEMBER_PW; | |
| 462 | 514 | } |
| 463 | 515 | } |
| 464 | 516 | }else{ |
| 465 | 517 | fossil_fatal("missing or incorrect password for user \"%s\"", |
| 466 | - g.urlUser); | |
| 518 | + pUrlData->user); | |
| 467 | 519 | } |
| 468 | 520 | } |
| 521 | + | |
| 522 | +/* | |
| 523 | +** Prompt the user for the password for g.urlUser. Store the result | |
| 524 | +** in g.urlPasswd. | |
| 525 | +*/ | |
| 526 | +void url_prompt_for_password(void){ | |
| 527 | + url_prompt_for_password_local(GLOBAL_URL()); | |
| 528 | +} | |
| 469 | 529 | |
| 470 | 530 | /* |
| 471 | 531 | ** Remember the URL and password if requested. |
| 472 | 532 | */ |
| 473 | 533 | void url_remember(void){ |
| 474 | 534 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -39,10 +39,34 @@ | |
| 39 | #define URL_REMEMBER 0x002 /* Remember the url for later reuse */ |
| 40 | #define URL_ASK_REMEMBER_PW 0x004 /* Ask whether to remember prompted pw */ |
| 41 | #define URL_REMEMBER_PW 0x008 /* Should remember pw */ |
| 42 | #define URL_PROMPTED 0x010 /* Prompted for PW already */ |
| 43 | |
| 44 | #endif /* INTERFACE */ |
| 45 | |
| 46 | |
| 47 | /* |
| 48 | ** Convert a string to lower-case. |
| @@ -51,10 +75,201 @@ | |
| 51 | while( *z ){ |
| 52 | *z = fossil_tolower(*z); |
| 53 | z++; |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | /* |
| 58 | ** Parse the given URL, which describes a sync server. Populate variables |
| 59 | ** in the global "g" structure as follows: |
| 60 | ** |
| @@ -79,175 +294,11 @@ | |
| 79 | ** |
| 80 | ** ssh://userid@host:port/path?fossil=path/to/fossil.exe |
| 81 | ** |
| 82 | */ |
| 83 | void url_parse(const char *zUrl, unsigned int urlFlags){ |
| 84 | int i, j, c; |
| 85 | char *zFile = 0; |
| 86 | |
| 87 | if( zUrl==0 ){ |
| 88 | zUrl = db_get("last-sync-url", 0); |
| 89 | if( zUrl==0 ) return; |
| 90 | if( g.urlPasswd==0 ){ |
| 91 | g.urlPasswd = unobscure(db_get("last-sync-pw", 0)); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | if( strncmp(zUrl, "http://", 7)==0 |
| 96 | || strncmp(zUrl, "https://", 8)==0 |
| 97 | || strncmp(zUrl, "ssh://", 6)==0 |
| 98 | ){ |
| 99 | int iStart; |
| 100 | char *zLogin; |
| 101 | char *zExe; |
| 102 | char cQuerySep = '?'; |
| 103 | |
| 104 | g.urlIsFile = 0; |
| 105 | if( zUrl[4]=='s' ){ |
| 106 | g.urlIsHttps = 1; |
| 107 | g.urlProtocol = "https"; |
| 108 | g.urlDfltPort = 443; |
| 109 | iStart = 8; |
| 110 | }else if( zUrl[0]=='s' ){ |
| 111 | g.urlIsSsh = 1; |
| 112 | g.urlProtocol = "ssh"; |
| 113 | g.urlDfltPort = 22; |
| 114 | g.urlFossil = "fossil"; |
| 115 | iStart = 6; |
| 116 | }else{ |
| 117 | g.urlIsHttps = 0; |
| 118 | g.urlProtocol = "http"; |
| 119 | g.urlDfltPort = 80; |
| 120 | iStart = 7; |
| 121 | } |
| 122 | for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} |
| 123 | if( c=='@' ){ |
| 124 | /* Parse up the user-id and password */ |
| 125 | for(j=iStart; j<i && zUrl[j]!=':'; j++){} |
| 126 | g.urlUser = mprintf("%.*s", j-iStart, &zUrl[iStart]); |
| 127 | dehttpize(g.urlUser); |
| 128 | if( j<i ){ |
| 129 | if( ( urlFlags & URL_REMEMBER ) && g.urlIsSsh==0 ){ |
| 130 | urlFlags |= URL_ASK_REMEMBER_PW; |
| 131 | } |
| 132 | g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]); |
| 133 | dehttpize(g.urlPasswd); |
| 134 | } |
| 135 | if( g.urlIsSsh ){ |
| 136 | urlFlags &= ~URL_ASK_REMEMBER_PW; |
| 137 | } |
| 138 | zLogin = mprintf("%t@", g.urlUser); |
| 139 | for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} |
| 140 | g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]); |
| 141 | i = j; |
| 142 | }else{ |
| 143 | for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){} |
| 144 | g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]); |
| 145 | zLogin = mprintf(""); |
| 146 | } |
| 147 | url_tolower(g.urlName); |
| 148 | if( c==':' ){ |
| 149 | g.urlPort = 0; |
| 150 | i++; |
| 151 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 152 | g.urlPort = g.urlPort*10 + c - '0'; |
| 153 | i++; |
| 154 | } |
| 155 | g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort); |
| 156 | }else{ |
| 157 | g.urlPort = g.urlDfltPort; |
| 158 | g.urlHostname = g.urlName; |
| 159 | } |
| 160 | dehttpize(g.urlName); |
| 161 | g.urlPath = mprintf("%s", &zUrl[i]); |
| 162 | for(i=0; g.urlPath[i] && g.urlPath[i]!='?'; i++){} |
| 163 | if( g.urlPath[i] ){ |
| 164 | g.urlPath[i] = 0; |
| 165 | i++; |
| 166 | } |
| 167 | zExe = mprintf(""); |
| 168 | while( g.urlPath[i]!=0 ){ |
| 169 | char *zName, *zValue; |
| 170 | zName = &g.urlPath[i]; |
| 171 | zValue = zName; |
| 172 | while( g.urlPath[i] && g.urlPath[i]!='=' ){ i++; } |
| 173 | if( g.urlPath[i]=='=' ){ |
| 174 | g.urlPath[i] = 0; |
| 175 | i++; |
| 176 | zValue = &g.urlPath[i]; |
| 177 | while( g.urlPath[i] && g.urlPath[i]!='&' ){ i++; } |
| 178 | } |
| 179 | if( g.urlPath[i] ){ |
| 180 | g.urlPath[i] = 0; |
| 181 | i++; |
| 182 | } |
| 183 | if( fossil_strcmp(zName,"fossil")==0 ){ |
| 184 | g.urlFossil = zValue; |
| 185 | dehttpize(g.urlFossil); |
| 186 | zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil); |
| 187 | cQuerySep = '&'; |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | dehttpize(g.urlPath); |
| 192 | if( g.urlDfltPort==g.urlPort ){ |
| 193 | g.urlCanonical = mprintf( |
| 194 | "%s://%s%T%T%s", |
| 195 | g.urlProtocol, zLogin, g.urlName, g.urlPath, zExe |
| 196 | ); |
| 197 | }else{ |
| 198 | g.urlCanonical = mprintf( |
| 199 | "%s://%s%T:%d%T%s", |
| 200 | g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath, zExe |
| 201 | ); |
| 202 | } |
| 203 | if( g.urlIsSsh && g.urlPath[1] ) g.urlPath++; |
| 204 | free(zLogin); |
| 205 | }else if( strncmp(zUrl, "file:", 5)==0 ){ |
| 206 | g.urlIsFile = 1; |
| 207 | if( zUrl[5]=='/' && zUrl[6]=='/' ){ |
| 208 | i = 7; |
| 209 | }else{ |
| 210 | i = 5; |
| 211 | } |
| 212 | zFile = mprintf("%s", &zUrl[i]); |
| 213 | }else if( file_isfile(zUrl) ){ |
| 214 | g.urlIsFile = 1; |
| 215 | zFile = mprintf("%s", zUrl); |
| 216 | }else if( file_isdir(zUrl)==1 ){ |
| 217 | zFile = mprintf("%s/FOSSIL", zUrl); |
| 218 | if( file_isfile(zFile) ){ |
| 219 | g.urlIsFile = 1; |
| 220 | }else{ |
| 221 | free(zFile); |
| 222 | fossil_fatal("unknown repository: %s", zUrl); |
| 223 | } |
| 224 | }else{ |
| 225 | fossil_fatal("unknown repository: %s", zUrl); |
| 226 | } |
| 227 | if( urlFlags ) g.urlFlags = urlFlags; |
| 228 | if( g.urlIsFile ){ |
| 229 | Blob cfile; |
| 230 | dehttpize(zFile); |
| 231 | file_canonical_name(zFile, &cfile, 0); |
| 232 | free(zFile); |
| 233 | g.urlProtocol = "file"; |
| 234 | g.urlPath = ""; |
| 235 | g.urlName = mprintf("%b", &cfile); |
| 236 | g.urlCanonical = mprintf("file://%T", g.urlName); |
| 237 | blob_reset(&cfile); |
| 238 | }else if( g.urlUser!=0 && g.urlPasswd==0 && (urlFlags & URL_PROMPT_PW) ){ |
| 239 | url_prompt_for_password(); |
| 240 | }else if( g.urlUser!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){ |
| 241 | if( isatty(fileno(stdin)) ){ |
| 242 | if( save_password_prompt() ){ |
| 243 | g.urlFlags = urlFlags |= URL_REMEMBER_PW; |
| 244 | }else{ |
| 245 | g.urlFlags = urlFlags &= ~URL_REMEMBER_PW; |
| 246 | } |
| 247 | } |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | /* |
| 252 | ** COMMAND: test-urlparser |
| 253 | ** |
| @@ -439,35 +490,44 @@ | |
| 439 | } |
| 440 | return blob_str(&p->url); |
| 441 | } |
| 442 | |
| 443 | /* |
| 444 | ** Prompt the user for the password for g.urlUser. Store the result |
| 445 | ** in g.urlPasswd. |
| 446 | */ |
| 447 | void url_prompt_for_password(void){ |
| 448 | if( g.urlIsSsh || g.urlIsFile ) return; |
| 449 | if( isatty(fileno(stdin)) |
| 450 | && (g.urlFlags & URL_PROMPT_PW)!=0 |
| 451 | && (g.urlFlags & URL_PROMPTED)==0 |
| 452 | ){ |
| 453 | g.urlFlags |= URL_PROMPTED; |
| 454 | g.urlPasswd = prompt_for_user_password(g.urlUser); |
| 455 | if( g.urlPasswd[0] |
| 456 | && (g.urlFlags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 |
| 457 | ){ |
| 458 | if( save_password_prompt() ){ |
| 459 | g.urlFlags |= URL_REMEMBER_PW; |
| 460 | }else{ |
| 461 | g.urlFlags &= ~URL_REMEMBER_PW; |
| 462 | } |
| 463 | } |
| 464 | }else{ |
| 465 | fossil_fatal("missing or incorrect password for user \"%s\"", |
| 466 | g.urlUser); |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | /* |
| 471 | ** Remember the URL and password if requested. |
| 472 | */ |
| 473 | void url_remember(void){ |
| 474 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -39,10 +39,34 @@ | |
| 39 | #define URL_REMEMBER 0x002 /* Remember the url for later reuse */ |
| 40 | #define URL_ASK_REMEMBER_PW 0x004 /* Ask whether to remember prompted pw */ |
| 41 | #define URL_REMEMBER_PW 0x008 /* Should remember pw */ |
| 42 | #define URL_PROMPTED 0x010 /* Prompted for PW already */ |
| 43 | |
| 44 | /* |
| 45 | ** The URL related data used with this subsystem. |
| 46 | */ |
| 47 | struct UrlData { |
| 48 | /* |
| 49 | ** NOTE: These members MUST be kept in sync with the related ones in the |
| 50 | ** "Global" structure defined in "main.c". |
| 51 | */ |
| 52 | int isFile; /* True if a "file:" url */ |
| 53 | int isHttps; /* True if a "https:" url */ |
| 54 | int isSsh; /* True if an "ssh:" url */ |
| 55 | char *name; /* Hostname for http: or filename for file: */ |
| 56 | char *hostname; /* The HOST: parameter on http headers */ |
| 57 | char *protocol; /* "http" or "https" */ |
| 58 | int port; /* TCP port number for http: or https: */ |
| 59 | int dfltPort; /* The default port for the given protocol */ |
| 60 | char *path; /* Pathname for http: */ |
| 61 | char *user; /* User id for http: */ |
| 62 | char *passwd; /* Password for http: */ |
| 63 | char *canonical; /* Canonical representation of the URL */ |
| 64 | char *proxyAuth; /* Proxy-Authorizer: string */ |
| 65 | char *fossil; /* The fossil query parameter on ssh: */ |
| 66 | unsigned flags; /* Boolean flags controlling URL processing */ |
| 67 | }; |
| 68 | #endif /* INTERFACE */ |
| 69 | |
| 70 | |
| 71 | /* |
| 72 | ** Convert a string to lower-case. |
| @@ -51,10 +75,201 @@ | |
| 75 | while( *z ){ |
| 76 | *z = fossil_tolower(*z); |
| 77 | z++; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 83 | ** as follows: |
| 84 | ** |
| 85 | ** isFile True if FILE: |
| 86 | ** isHttps True if HTTPS: |
| 87 | ** isSsh True if SSH: |
| 88 | ** protocol "http" or "https" or "file" |
| 89 | ** name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: |
| 90 | ** port TCP port number for HTTP or HTTPS. |
| 91 | ** dfltPort Default TCP port number (80 or 443). |
| 92 | ** path Path name for HTTP or HTTPS. |
| 93 | ** user Userid. |
| 94 | ** passwd Password. |
| 95 | ** hostname HOST:PORT or just HOST if port is the default. |
| 96 | ** canonical The URL in canonical form, omitting the password |
| 97 | ** |
| 98 | */ |
| 99 | void url_parse_local( |
| 100 | const char *zUrl, |
| 101 | unsigned int urlFlags, |
| 102 | UrlData *pUrlData |
| 103 | ){ |
| 104 | int i, j, c; |
| 105 | char *zFile = 0; |
| 106 | |
| 107 | if( zUrl==0 ){ |
| 108 | zUrl = db_get("last-sync-url", 0); |
| 109 | if( zUrl==0 ) return; |
| 110 | if( pUrlData->passwd==0 ){ |
| 111 | pUrlData->passwd = unobscure(db_get("last-sync-pw", 0)); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | if( strncmp(zUrl, "http://", 7)==0 |
| 116 | || strncmp(zUrl, "https://", 8)==0 |
| 117 | || strncmp(zUrl, "ssh://", 6)==0 |
| 118 | ){ |
| 119 | int iStart; |
| 120 | char *zLogin; |
| 121 | char *zExe; |
| 122 | char cQuerySep = '?'; |
| 123 | |
| 124 | pUrlData->isFile = 0; |
| 125 | if( zUrl[4]=='s' ){ |
| 126 | pUrlData->isHttps = 1; |
| 127 | pUrlData->protocol = "https"; |
| 128 | pUrlData->dfltPort = 443; |
| 129 | iStart = 8; |
| 130 | }else if( zUrl[0]=='s' ){ |
| 131 | pUrlData->isSsh = 1; |
| 132 | pUrlData->protocol = "ssh"; |
| 133 | pUrlData->dfltPort = 22; |
| 134 | pUrlData->fossil = "fossil"; |
| 135 | iStart = 6; |
| 136 | }else{ |
| 137 | pUrlData->isHttps = 0; |
| 138 | pUrlData->protocol = "http"; |
| 139 | pUrlData->dfltPort = 80; |
| 140 | iStart = 7; |
| 141 | } |
| 142 | for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} |
| 143 | if( c=='@' ){ |
| 144 | /* Parse up the user-id and password */ |
| 145 | for(j=iStart; j<i && zUrl[j]!=':'; j++){} |
| 146 | pUrlData->user = mprintf("%.*s", j-iStart, &zUrl[iStart]); |
| 147 | dehttpize(pUrlData->user); |
| 148 | if( j<i ){ |
| 149 | if( ( urlFlags & URL_REMEMBER ) && pUrlData->isSsh==0 ){ |
| 150 | urlFlags |= URL_ASK_REMEMBER_PW; |
| 151 | } |
| 152 | pUrlData->passwd = mprintf("%.*s", i-j-1, &zUrl[j+1]); |
| 153 | dehttpize(pUrlData->passwd); |
| 154 | } |
| 155 | if( pUrlData->isSsh ){ |
| 156 | urlFlags &= ~URL_ASK_REMEMBER_PW; |
| 157 | } |
| 158 | zLogin = mprintf("%t@", pUrlData->user); |
| 159 | for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} |
| 160 | pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]); |
| 161 | i = j; |
| 162 | }else{ |
| 163 | for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){} |
| 164 | pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]); |
| 165 | zLogin = mprintf(""); |
| 166 | } |
| 167 | url_tolower(pUrlData->name); |
| 168 | if( c==':' ){ |
| 169 | pUrlData->port = 0; |
| 170 | i++; |
| 171 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 172 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| 173 | i++; |
| 174 | } |
| 175 | pUrlData->hostname = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 176 | }else{ |
| 177 | pUrlData->port = pUrlData->dfltPort; |
| 178 | pUrlData->hostname = pUrlData->name; |
| 179 | } |
| 180 | dehttpize(pUrlData->name); |
| 181 | pUrlData->path = mprintf("%s", &zUrl[i]); |
| 182 | for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){} |
| 183 | if( pUrlData->path[i] ){ |
| 184 | pUrlData->path[i] = 0; |
| 185 | i++; |
| 186 | } |
| 187 | zExe = mprintf(""); |
| 188 | while( pUrlData->path[i]!=0 ){ |
| 189 | char *zName, *zValue; |
| 190 | zName = &pUrlData->path[i]; |
| 191 | zValue = zName; |
| 192 | while( pUrlData->path[i] && pUrlData->path[i]!='=' ){ i++; } |
| 193 | if( pUrlData->path[i]=='=' ){ |
| 194 | pUrlData->path[i] = 0; |
| 195 | i++; |
| 196 | zValue = &pUrlData->path[i]; |
| 197 | while( pUrlData->path[i] && pUrlData->path[i]!='&' ){ i++; } |
| 198 | } |
| 199 | if( pUrlData->path[i] ){ |
| 200 | pUrlData->path[i] = 0; |
| 201 | i++; |
| 202 | } |
| 203 | if( fossil_strcmp(zName,"fossil")==0 ){ |
| 204 | pUrlData->fossil = zValue; |
| 205 | dehttpize(pUrlData->fossil); |
| 206 | zExe = mprintf("%cfossil=%T", cQuerySep, pUrlData->fossil); |
| 207 | cQuerySep = '&'; |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | dehttpize(pUrlData->path); |
| 212 | if( pUrlData->dfltPort==pUrlData->port ){ |
| 213 | pUrlData->canonical = mprintf( |
| 214 | "%s://%s%T%T%s", |
| 215 | pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe |
| 216 | ); |
| 217 | }else{ |
| 218 | pUrlData->canonical = mprintf( |
| 219 | "%s://%s%T:%d%T%s", |
| 220 | pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port, |
| 221 | pUrlData->path, zExe |
| 222 | ); |
| 223 | } |
| 224 | if( pUrlData->isSsh && pUrlData->path[1] ) pUrlData->path++; |
| 225 | free(zLogin); |
| 226 | }else if( strncmp(zUrl, "file:", 5)==0 ){ |
| 227 | pUrlData->isFile = 1; |
| 228 | if( zUrl[5]=='/' && zUrl[6]=='/' ){ |
| 229 | i = 7; |
| 230 | }else{ |
| 231 | i = 5; |
| 232 | } |
| 233 | zFile = mprintf("%s", &zUrl[i]); |
| 234 | }else if( file_isfile(zUrl) ){ |
| 235 | pUrlData->isFile = 1; |
| 236 | zFile = mprintf("%s", zUrl); |
| 237 | }else if( file_isdir(zUrl)==1 ){ |
| 238 | zFile = mprintf("%s/FOSSIL", zUrl); |
| 239 | if( file_isfile(zFile) ){ |
| 240 | pUrlData->isFile = 1; |
| 241 | }else{ |
| 242 | free(zFile); |
| 243 | fossil_fatal("unknown repository: %s", zUrl); |
| 244 | } |
| 245 | }else{ |
| 246 | fossil_fatal("unknown repository: %s", zUrl); |
| 247 | } |
| 248 | if( urlFlags ) pUrlData->flags = urlFlags; |
| 249 | if( pUrlData->isFile ){ |
| 250 | Blob cfile; |
| 251 | dehttpize(zFile); |
| 252 | file_canonical_name(zFile, &cfile, 0); |
| 253 | free(zFile); |
| 254 | pUrlData->protocol = "file"; |
| 255 | pUrlData->path = ""; |
| 256 | pUrlData->name = mprintf("%b", &cfile); |
| 257 | pUrlData->canonical = mprintf("file://%T", pUrlData->name); |
| 258 | blob_reset(&cfile); |
| 259 | }else if( pUrlData->user!=0 && pUrlData->passwd==0 && (urlFlags & URL_PROMPT_PW) ){ |
| 260 | url_prompt_for_password_local(pUrlData); |
| 261 | }else if( pUrlData->user!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){ |
| 262 | if( isatty(fileno(stdin)) ){ |
| 263 | if( save_password_prompt(pUrlData->passwd) ){ |
| 264 | pUrlData->flags = urlFlags |= URL_REMEMBER_PW; |
| 265 | }else{ |
| 266 | pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW; |
| 267 | } |
| 268 | } |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | /* |
| 273 | ** Parse the given URL, which describes a sync server. Populate variables |
| 274 | ** in the global "g" structure as follows: |
| 275 | ** |
| @@ -79,175 +294,11 @@ | |
| 294 | ** |
| 295 | ** ssh://userid@host:port/path?fossil=path/to/fossil.exe |
| 296 | ** |
| 297 | */ |
| 298 | void url_parse(const char *zUrl, unsigned int urlFlags){ |
| 299 | url_parse_local(zUrl, urlFlags, GLOBAL_URL()); |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | ** COMMAND: test-urlparser |
| 304 | ** |
| @@ -439,35 +490,44 @@ | |
| 490 | } |
| 491 | return blob_str(&p->url); |
| 492 | } |
| 493 | |
| 494 | /* |
| 495 | ** Prompt the user for the password that corresponds to the "user" member of |
| 496 | ** the provided UrlData structure. Store the result into the "passwd" member |
| 497 | ** of the provided UrlData structure. |
| 498 | */ |
| 499 | void url_prompt_for_password_local(UrlData *pUrlData){ |
| 500 | if( pUrlData->isSsh || pUrlData->isFile ) return; |
| 501 | if( isatty(fileno(stdin)) |
| 502 | && (pUrlData->flags & URL_PROMPT_PW)!=0 |
| 503 | && (pUrlData->flags & URL_PROMPTED)==0 |
| 504 | ){ |
| 505 | pUrlData->flags |= URL_PROMPTED; |
| 506 | pUrlData->passwd = prompt_for_user_password(pUrlData->user); |
| 507 | if( pUrlData->passwd[0] |
| 508 | && (pUrlData->flags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 |
| 509 | ){ |
| 510 | if( save_password_prompt(pUrlData->passwd) ){ |
| 511 | pUrlData->flags |= URL_REMEMBER_PW; |
| 512 | }else{ |
| 513 | pUrlData->flags &= ~URL_REMEMBER_PW; |
| 514 | } |
| 515 | } |
| 516 | }else{ |
| 517 | fossil_fatal("missing or incorrect password for user \"%s\"", |
| 518 | pUrlData->user); |
| 519 | } |
| 520 | } |
| 521 | |
| 522 | /* |
| 523 | ** Prompt the user for the password for g.urlUser. Store the result |
| 524 | ** in g.urlPasswd. |
| 525 | */ |
| 526 | void url_prompt_for_password(void){ |
| 527 | url_prompt_for_password_local(GLOBAL_URL()); |
| 528 | } |
| 529 | |
| 530 | /* |
| 531 | ** Remember the URL and password if requested. |
| 532 | */ |
| 533 | void url_remember(void){ |
| 534 |
+2
-2
| --- src/user.c | ||
| +++ src/user.c | ||
| @@ -132,15 +132,15 @@ | ||
| 132 | 132 | } |
| 133 | 133 | |
| 134 | 134 | /* |
| 135 | 135 | ** Prompt to save Fossil user password |
| 136 | 136 | */ |
| 137 | -int save_password_prompt(){ | |
| 137 | +int save_password_prompt(const char *passwd){ | |
| 138 | 138 | Blob x; |
| 139 | 139 | char c; |
| 140 | 140 | const char *old = db_get("last-sync-pw", 0); |
| 141 | - if( (old!=0) && fossil_strcmp(unobscure(old), g.urlPasswd)==0 ){ | |
| 141 | + if( (old!=0) && fossil_strcmp(unobscure(old), passwd)==0 ){ | |
| 142 | 142 | return 0; |
| 143 | 143 | } |
| 144 | 144 | prompt_user("remember password (Y/n)? ", &x); |
| 145 | 145 | c = blob_str(&x)[0]; |
| 146 | 146 | blob_reset(&x); |
| 147 | 147 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -132,15 +132,15 @@ | |
| 132 | } |
| 133 | |
| 134 | /* |
| 135 | ** Prompt to save Fossil user password |
| 136 | */ |
| 137 | int save_password_prompt(){ |
| 138 | Blob x; |
| 139 | char c; |
| 140 | const char *old = db_get("last-sync-pw", 0); |
| 141 | if( (old!=0) && fossil_strcmp(unobscure(old), g.urlPasswd)==0 ){ |
| 142 | return 0; |
| 143 | } |
| 144 | prompt_user("remember password (Y/n)? ", &x); |
| 145 | c = blob_str(&x)[0]; |
| 146 | blob_reset(&x); |
| 147 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -132,15 +132,15 @@ | |
| 132 | } |
| 133 | |
| 134 | /* |
| 135 | ** Prompt to save Fossil user password |
| 136 | */ |
| 137 | int save_password_prompt(const char *passwd){ |
| 138 | Blob x; |
| 139 | char c; |
| 140 | const char *old = db_get("last-sync-pw", 0); |
| 141 | if( (old!=0) && fossil_strcmp(unobscure(old), passwd)==0 ){ |
| 142 | return 0; |
| 143 | } |
| 144 | prompt_user("remember password (Y/n)? ", &x); |
| 145 | c = blob_str(&x)[0]; |
| 146 | blob_reset(&x); |
| 147 |
+1
-1
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -294,11 +294,11 @@ | ||
| 294 | 294 | moderation_table_create(); |
| 295 | 295 | db_multi_exec("INSERT INTO modreq(objid) VALUES(%d)", nrid); |
| 296 | 296 | } |
| 297 | 297 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 298 | 298 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid); |
| 299 | - manifest_crosslink(nrid, pWiki); | |
| 299 | + manifest_crosslink(nrid, pWiki, MC_NONE); | |
| 300 | 300 | } |
| 301 | 301 | |
| 302 | 302 | /* |
| 303 | 303 | ** Formal names and common names for the various wiki styles. |
| 304 | 304 | */ |
| 305 | 305 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -294,11 +294,11 @@ | |
| 294 | moderation_table_create(); |
| 295 | db_multi_exec("INSERT INTO modreq(objid) VALUES(%d)", nrid); |
| 296 | } |
| 297 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 298 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid); |
| 299 | manifest_crosslink(nrid, pWiki); |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | ** Formal names and common names for the various wiki styles. |
| 304 | */ |
| 305 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -294,11 +294,11 @@ | |
| 294 | moderation_table_create(); |
| 295 | db_multi_exec("INSERT INTO modreq(objid) VALUES(%d)", nrid); |
| 296 | } |
| 297 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 298 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid); |
| 299 | manifest_crosslink(nrid, pWiki, MC_NONE); |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | ** Formal names and common names for the various wiki styles. |
| 304 | */ |
| 305 |
+77
-32
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -191,11 +191,11 @@ | ||
| 191 | 191 | if( rid==0 ){ |
| 192 | 192 | blob_appendf(&pXfer->err, "%s", g.zErrMsg); |
| 193 | 193 | blob_reset(&content); |
| 194 | 194 | }else{ |
| 195 | 195 | if( !isPriv ) content_make_public(rid); |
| 196 | - manifest_crosslink(rid, &content); | |
| 196 | + manifest_crosslink(rid, &content, MC_NONE); | |
| 197 | 197 | } |
| 198 | 198 | assert( blob_is_reset(&content) ); |
| 199 | 199 | remote_has(rid); |
| 200 | 200 | } |
| 201 | 201 | |
| @@ -820,33 +820,73 @@ | ||
| 820 | 820 | static void server_private_xfer_not_authorized(void){ |
| 821 | 821 | @ error not\sauthorized\sto\ssync\sprivate\scontent |
| 822 | 822 | } |
| 823 | 823 | |
| 824 | 824 | /* |
| 825 | -** Run the specified TH1 script, if any, and returns the return code or TH_OK | |
| 826 | -** when there is no script. | |
| 827 | -*/ | |
| 828 | -static int run_script(const char *zScript){ | |
| 829 | - if( !zScript ){ | |
| 830 | - return TH_OK; /* No script, return success. */ | |
| 831 | - } | |
| 832 | - Th_FossilInit(TH_INIT_DEFAULT); /* Make sure TH1 is ready. */ | |
| 833 | - return Th_Eval(g.interp, 0, zScript, -1); | |
| 834 | -} | |
| 835 | - | |
| 836 | -/* | |
| 837 | -** Run the pre-transfer TH1 script, if any, and returns the return code. | |
| 838 | -*/ | |
| 839 | -static int run_common_script(void){ | |
| 840 | - return run_script(db_get("xfer-common-script", 0)); | |
| 841 | -} | |
| 842 | - | |
| 843 | -/* | |
| 844 | -** Run the post-push TH1 script, if any, and returns the return code. | |
| 845 | -*/ | |
| 846 | -static int run_push_script(void){ | |
| 847 | - return run_script(db_get("xfer-push-script", 0)); | |
| 825 | +** Return the common TH1 code to evaluate prior to evaluating any other | |
| 826 | +** TH1 transfer notification scripts. | |
| 827 | +*/ | |
| 828 | +const char *xfer_common_code(void){ | |
| 829 | + return db_get("xfer-common-script", 0); | |
| 830 | +} | |
| 831 | + | |
| 832 | +/* | |
| 833 | +** Return the TH1 code to evaluate when a push is processed. | |
| 834 | +*/ | |
| 835 | +const char *xfer_push_code(void){ | |
| 836 | + return db_get("xfer-push-script", 0); | |
| 837 | +} | |
| 838 | + | |
| 839 | +/* | |
| 840 | +** Return the TH1 code to evaluate when a commit is processed. | |
| 841 | +*/ | |
| 842 | +const char *xfer_commit_code(void){ | |
| 843 | + return db_get("xfer-commit-script", 0); | |
| 844 | +} | |
| 845 | + | |
| 846 | +/* | |
| 847 | +** Return the TH1 code to evaluate when a ticket change is processed. | |
| 848 | +*/ | |
| 849 | +const char *xfer_ticket_code(void){ | |
| 850 | + return db_get("xfer-ticket-script", 0); | |
| 851 | +} | |
| 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 | |
| 877 | +** that cannot be redone, it should use an internal [if] guard similar to | |
| 878 | +** the following: | |
| 879 | +** | |
| 880 | +** if {![info exists common_done]} { | |
| 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); | |
| 848 | 888 | } |
| 849 | 889 | |
| 850 | 890 | /* |
| 851 | 891 | ** If this variable is set, disable login checks. Used for debugging |
| 852 | 892 | ** only. |
| @@ -875,10 +915,11 @@ | ||
| 875 | 915 | int isClone = 0; |
| 876 | 916 | int nGimme = 0; |
| 877 | 917 | int size; |
| 878 | 918 | int recvConfig = 0; |
| 879 | 919 | char *zNow; |
| 920 | + int result; | |
| 880 | 921 | |
| 881 | 922 | if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 882 | 923 | fossil_redirect_home(); |
| 883 | 924 | } |
| 884 | 925 | g.zLogin = "anonymous"; |
| @@ -904,13 +945,14 @@ | ||
| 904 | 945 | db_begin_transaction(); |
| 905 | 946 | db_multi_exec( |
| 906 | 947 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 907 | 948 | ); |
| 908 | 949 | manifest_crosslink_begin(); |
| 909 | - if( run_common_script()==TH_ERROR ){ | |
| 950 | + result = xfer_run_common_script(); | |
| 951 | + if( result==TH_ERROR ){ | |
| 910 | 952 | cgi_reset_content(); |
| 911 | - @ error common\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0)) | |
| 953 | + @ error common\sscript\sfailed:\s%F(g.zErrMsg) | |
| 912 | 954 | nErr++; |
| 913 | 955 | } |
| 914 | 956 | while( blob_line(xfer.pIn, &xfer.line) ){ |
| 915 | 957 | if( blob_buffer(&xfer.line)[0]=='#' ) continue; |
| 916 | 958 | if( blob_size(&xfer.line)==0 ) continue; |
| @@ -1231,14 +1273,17 @@ | ||
| 1231 | 1273 | } |
| 1232 | 1274 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 1233 | 1275 | blob_reset(&xfer.line); |
| 1234 | 1276 | } |
| 1235 | 1277 | if( isPush ){ |
| 1236 | - if( run_push_script()==TH_ERROR ){ | |
| 1237 | - cgi_reset_content(); | |
| 1238 | - @ error push\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0)) | |
| 1239 | - nErr++; | |
| 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 | + } | |
| 1240 | 1285 | } |
| 1241 | 1286 | request_phantoms(&xfer, 500); |
| 1242 | 1287 | } |
| 1243 | 1288 | if( isClone && nGimme==0 ){ |
| 1244 | 1289 | /* The initial "clone" message from client to server contains no |
| @@ -1881,13 +1926,13 @@ | ||
| 1881 | 1926 | |
| 1882 | 1927 | fossil_force_newline(); |
| 1883 | 1928 | fossil_print( |
| 1884 | 1929 | "%s finished with %lld bytes sent, %lld bytes received\n", |
| 1885 | 1930 | zOpType, nSent, nRcvd); |
| 1886 | - transport_close(); | |
| 1887 | - transport_global_shutdown(); | |
| 1931 | + transport_close(GLOBAL_URL()); | |
| 1932 | + transport_global_shutdown(GLOBAL_URL()); | |
| 1888 | 1933 | db_multi_exec("DROP TABLE onremote"); |
| 1889 | 1934 | manifest_crosslink_end(); |
| 1890 | 1935 | content_enable_dephantomize(1); |
| 1891 | 1936 | db_end_transaction(0); |
| 1892 | 1937 | return nErr; |
| 1893 | 1938 | } |
| 1894 | 1939 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -191,11 +191,11 @@ | |
| 191 | if( rid==0 ){ |
| 192 | blob_appendf(&pXfer->err, "%s", g.zErrMsg); |
| 193 | blob_reset(&content); |
| 194 | }else{ |
| 195 | if( !isPriv ) content_make_public(rid); |
| 196 | manifest_crosslink(rid, &content); |
| 197 | } |
| 198 | assert( blob_is_reset(&content) ); |
| 199 | remote_has(rid); |
| 200 | } |
| 201 | |
| @@ -820,33 +820,73 @@ | |
| 820 | static void server_private_xfer_not_authorized(void){ |
| 821 | @ error not\sauthorized\sto\ssync\sprivate\scontent |
| 822 | } |
| 823 | |
| 824 | /* |
| 825 | ** Run the specified TH1 script, if any, and returns the return code or TH_OK |
| 826 | ** when there is no script. |
| 827 | */ |
| 828 | static int run_script(const char *zScript){ |
| 829 | if( !zScript ){ |
| 830 | return TH_OK; /* No script, return success. */ |
| 831 | } |
| 832 | Th_FossilInit(TH_INIT_DEFAULT); /* Make sure TH1 is ready. */ |
| 833 | return Th_Eval(g.interp, 0, zScript, -1); |
| 834 | } |
| 835 | |
| 836 | /* |
| 837 | ** Run the pre-transfer TH1 script, if any, and returns the return code. |
| 838 | */ |
| 839 | static int run_common_script(void){ |
| 840 | return run_script(db_get("xfer-common-script", 0)); |
| 841 | } |
| 842 | |
| 843 | /* |
| 844 | ** Run the post-push TH1 script, if any, and returns the return code. |
| 845 | */ |
| 846 | static int run_push_script(void){ |
| 847 | return run_script(db_get("xfer-push-script", 0)); |
| 848 | } |
| 849 | |
| 850 | /* |
| 851 | ** If this variable is set, disable login checks. Used for debugging |
| 852 | ** only. |
| @@ -875,10 +915,11 @@ | |
| 875 | int isClone = 0; |
| 876 | int nGimme = 0; |
| 877 | int size; |
| 878 | int recvConfig = 0; |
| 879 | char *zNow; |
| 880 | |
| 881 | if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 882 | fossil_redirect_home(); |
| 883 | } |
| 884 | g.zLogin = "anonymous"; |
| @@ -904,13 +945,14 @@ | |
| 904 | db_begin_transaction(); |
| 905 | db_multi_exec( |
| 906 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 907 | ); |
| 908 | manifest_crosslink_begin(); |
| 909 | if( run_common_script()==TH_ERROR ){ |
| 910 | cgi_reset_content(); |
| 911 | @ error common\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0)) |
| 912 | nErr++; |
| 913 | } |
| 914 | while( blob_line(xfer.pIn, &xfer.line) ){ |
| 915 | if( blob_buffer(&xfer.line)[0]=='#' ) continue; |
| 916 | if( blob_size(&xfer.line)==0 ) continue; |
| @@ -1231,14 +1273,17 @@ | |
| 1231 | } |
| 1232 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 1233 | blob_reset(&xfer.line); |
| 1234 | } |
| 1235 | if( isPush ){ |
| 1236 | if( run_push_script()==TH_ERROR ){ |
| 1237 | cgi_reset_content(); |
| 1238 | @ error push\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0)) |
| 1239 | nErr++; |
| 1240 | } |
| 1241 | request_phantoms(&xfer, 500); |
| 1242 | } |
| 1243 | if( isClone && nGimme==0 ){ |
| 1244 | /* The initial "clone" message from client to server contains no |
| @@ -1881,13 +1926,13 @@ | |
| 1881 | |
| 1882 | fossil_force_newline(); |
| 1883 | fossil_print( |
| 1884 | "%s finished with %lld bytes sent, %lld bytes received\n", |
| 1885 | zOpType, nSent, nRcvd); |
| 1886 | transport_close(); |
| 1887 | transport_global_shutdown(); |
| 1888 | db_multi_exec("DROP TABLE onremote"); |
| 1889 | manifest_crosslink_end(); |
| 1890 | content_enable_dephantomize(1); |
| 1891 | db_end_transaction(0); |
| 1892 | return nErr; |
| 1893 | } |
| 1894 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -191,11 +191,11 @@ | |
| 191 | if( rid==0 ){ |
| 192 | blob_appendf(&pXfer->err, "%s", g.zErrMsg); |
| 193 | blob_reset(&content); |
| 194 | }else{ |
| 195 | if( !isPriv ) content_make_public(rid); |
| 196 | manifest_crosslink(rid, &content, MC_NONE); |
| 197 | } |
| 198 | assert( blob_is_reset(&content) ); |
| 199 | remote_has(rid); |
| 200 | } |
| 201 | |
| @@ -820,33 +820,73 @@ | |
| 820 | static void server_private_xfer_not_authorized(void){ |
| 821 | @ error not\sauthorized\sto\ssync\sprivate\scontent |
| 822 | } |
| 823 | |
| 824 | /* |
| 825 | ** Return the common TH1 code to evaluate prior to evaluating any other |
| 826 | ** TH1 transfer notification scripts. |
| 827 | */ |
| 828 | const char *xfer_common_code(void){ |
| 829 | return db_get("xfer-common-script", 0); |
| 830 | } |
| 831 | |
| 832 | /* |
| 833 | ** Return the TH1 code to evaluate when a push is processed. |
| 834 | */ |
| 835 | const char *xfer_push_code(void){ |
| 836 | return db_get("xfer-push-script", 0); |
| 837 | } |
| 838 | |
| 839 | /* |
| 840 | ** Return the TH1 code to evaluate when a commit is processed. |
| 841 | */ |
| 842 | const char *xfer_commit_code(void){ |
| 843 | return db_get("xfer-commit-script", 0); |
| 844 | } |
| 845 | |
| 846 | /* |
| 847 | ** Return the TH1 code to evaluate when a ticket change is processed. |
| 848 | */ |
| 849 | const char *xfer_ticket_code(void){ |
| 850 | return db_get("xfer-ticket-script", 0); |
| 851 | } |
| 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 |
| 877 | ** that cannot be redone, it should use an internal [if] guard similar to |
| 878 | ** the following: |
| 879 | ** |
| 880 | ** if {![info exists common_done]} { |
| 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 |
| 892 | ** only. |
| @@ -875,10 +915,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"; |
| @@ -904,13 +945,14 @@ | |
| 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) ){ |
| 957 | if( blob_buffer(&xfer.line)[0]=='#' ) continue; |
| 958 | if( blob_size(&xfer.line)==0 ) continue; |
| @@ -1231,14 +1273,17 @@ | |
| 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 | } |
| 1286 | request_phantoms(&xfer, 500); |
| 1287 | } |
| 1288 | if( isClone && nGimme==0 ){ |
| 1289 | /* The initial "clone" message from client to server contains no |
| @@ -1881,13 +1926,13 @@ | |
| 1926 | |
| 1927 | fossil_force_newline(); |
| 1928 | fossil_print( |
| 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 |
+87
| --- src/xfersetup.c | ||
| +++ src/xfersetup.c | ||
| @@ -31,16 +31,63 @@ | ||
| 31 | 31 | if( !g.perm.Setup ){ |
| 32 | 32 | login_needed(); |
| 33 | 33 | } |
| 34 | 34 | |
| 35 | 35 | style_header("Transfer Setup"); |
| 36 | + | |
| 36 | 37 | @ <table border="0" cellspacing="20"> |
| 37 | 38 | setup_menu_entry("Common", "xfersetup_com", |
| 38 | 39 | "Common TH1 code run before all transfer request processing."); |
| 39 | 40 | setup_menu_entry("Push", "xfersetup_push", |
| 40 | 41 | "Specific TH1 code to run after \"push\" transfer requests."); |
| 42 | + setup_menu_entry("Commit", "xfersetup_commit", | |
| 43 | + "Specific TH1 code to run after processing a commit."); | |
| 44 | + setup_menu_entry("Ticket", "xfersetup_ticket", | |
| 45 | + "Specific TH1 code to run after processing a ticket change."); | |
| 41 | 46 | @ </table> |
| 47 | + | |
| 48 | + url_parse(0, 0); | |
| 49 | + if( g.urlProtocol ){ | |
| 50 | + unsigned syncFlags; | |
| 51 | + const char *zButton; | |
| 52 | + char *zWarning; | |
| 53 | + | |
| 54 | + if( db_get_boolean("dont-push", 0) ){ | |
| 55 | + syncFlags = SYNC_PULL; | |
| 56 | + zButton = "Pull"; | |
| 57 | + zWarning = 0; | |
| 58 | + }else{ | |
| 59 | + syncFlags = SYNC_PUSH | SYNC_PULL; | |
| 60 | + zButton = "Synchronize"; | |
| 61 | + zWarning = mprintf("WARNING: Pushing to \"%s\" is enabled.", | |
| 62 | + g.urlCanonical); | |
| 63 | + } | |
| 64 | + if( P("sync") ){ | |
| 65 | + user_select(); | |
| 66 | + url_enable_proxy(0); | |
| 67 | + client_sync(syncFlags, 0, 0); | |
| 68 | + } | |
| 69 | + @ <p>Press the %h(zButton) button below to synchronize with the | |
| 70 | + @ "%h(g.urlCanonical)" repository now. This may be useful when | |
| 71 | + @ testing the various transfer scripts.</p> | |
| 72 | + @ <p>You can use the "http -async" command in your scripts, but | |
| 73 | + @ make sure the "th1-uri-regexp" setting is set first.</p> | |
| 74 | + if( zWarning ){ | |
| 75 | + @ | |
| 76 | + @ <big><b>%h(zWarning)</b></big> | |
| 77 | + free(zWarning); | |
| 78 | + } | |
| 79 | + @ | |
| 80 | + @ <blockquote> | |
| 81 | + @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> | |
| 82 | + login_insert_csrf_secret(); | |
| 83 | + @ <input type="submit" name="sync" value="%h(zButton)" /> | |
| 84 | + @ </div></form> | |
| 85 | + @ </blockquote> | |
| 86 | + @ | |
| 87 | + } | |
| 88 | + | |
| 42 | 89 | style_footer(); |
| 43 | 90 | } |
| 44 | 91 | |
| 45 | 92 | /* |
| 46 | 93 | ** Common implementation for the transfer setup editor pages. |
| @@ -140,9 +187,49 @@ | ||
| 140 | 187 | "Transfer Push Script", |
| 141 | 188 | "xfer-push-script", |
| 142 | 189 | zDefaultXferPush, |
| 143 | 190 | zDesc, |
| 144 | 191 | 0, |
| 192 | + 0, | |
| 193 | + 30 | |
| 194 | + ); | |
| 195 | +} | |
| 196 | + | |
| 197 | +static const char *zDefaultXferCommit = 0; | |
| 198 | + | |
| 199 | +/* | |
| 200 | +** WEBPAGE: xfersetup_commit | |
| 201 | +*/ | |
| 202 | +void xfersetup_commit_page(void){ | |
| 203 | + static const char zDesc[] = | |
| 204 | + @ Enter TH1 script that runs when a commit is processed. | |
| 205 | + ; | |
| 206 | + xfersetup_generic( | |
| 207 | + "Transfer Commit Script", | |
| 208 | + "xfer-commit-script", | |
| 209 | + zDefaultXferCommit, | |
| 210 | + zDesc, | |
| 211 | + 0, | |
| 212 | + 0, | |
| 213 | + 30 | |
| 214 | + ); | |
| 215 | +} | |
| 216 | + | |
| 217 | +static const char *zDefaultXferTicket = 0; | |
| 218 | + | |
| 219 | +/* | |
| 220 | +** WEBPAGE: xfersetup_ticket | |
| 221 | +*/ | |
| 222 | +void xfersetup_ticket_page(void){ | |
| 223 | + static const char zDesc[] = | |
| 224 | + @ Enter TH1 script that runs when a ticket change is processed. | |
| 225 | + ; | |
| 226 | + xfersetup_generic( | |
| 227 | + "Transfer Ticket Script", | |
| 228 | + "xfer-ticket-script", | |
| 229 | + zDefaultXferTicket, | |
| 230 | + zDesc, | |
| 231 | + 0, | |
| 145 | 232 | 0, |
| 146 | 233 | 30 |
| 147 | 234 | ); |
| 148 | 235 | } |
| 149 | 236 |
| --- src/xfersetup.c | |
| +++ src/xfersetup.c | |
| @@ -31,16 +31,63 @@ | |
| 31 | if( !g.perm.Setup ){ |
| 32 | login_needed(); |
| 33 | } |
| 34 | |
| 35 | style_header("Transfer Setup"); |
| 36 | @ <table border="0" cellspacing="20"> |
| 37 | setup_menu_entry("Common", "xfersetup_com", |
| 38 | "Common TH1 code run before all transfer request processing."); |
| 39 | setup_menu_entry("Push", "xfersetup_push", |
| 40 | "Specific TH1 code to run after \"push\" transfer requests."); |
| 41 | @ </table> |
| 42 | style_footer(); |
| 43 | } |
| 44 | |
| 45 | /* |
| 46 | ** Common implementation for the transfer setup editor pages. |
| @@ -140,9 +187,49 @@ | |
| 140 | "Transfer Push Script", |
| 141 | "xfer-push-script", |
| 142 | zDefaultXferPush, |
| 143 | zDesc, |
| 144 | 0, |
| 145 | 0, |
| 146 | 30 |
| 147 | ); |
| 148 | } |
| 149 |
| --- src/xfersetup.c | |
| +++ src/xfersetup.c | |
| @@ -31,16 +31,63 @@ | |
| 31 | if( !g.perm.Setup ){ |
| 32 | login_needed(); |
| 33 | } |
| 34 | |
| 35 | style_header("Transfer Setup"); |
| 36 | |
| 37 | @ <table border="0" cellspacing="20"> |
| 38 | setup_menu_entry("Common", "xfersetup_com", |
| 39 | "Common TH1 code run before all transfer request processing."); |
| 40 | setup_menu_entry("Push", "xfersetup_push", |
| 41 | "Specific TH1 code to run after \"push\" transfer requests."); |
| 42 | setup_menu_entry("Commit", "xfersetup_commit", |
| 43 | "Specific TH1 code to run after processing a commit."); |
| 44 | setup_menu_entry("Ticket", "xfersetup_ticket", |
| 45 | "Specific TH1 code to run after processing a ticket change."); |
| 46 | @ </table> |
| 47 | |
| 48 | url_parse(0, 0); |
| 49 | if( g.urlProtocol ){ |
| 50 | unsigned syncFlags; |
| 51 | const char *zButton; |
| 52 | char *zWarning; |
| 53 | |
| 54 | if( db_get_boolean("dont-push", 0) ){ |
| 55 | syncFlags = SYNC_PULL; |
| 56 | zButton = "Pull"; |
| 57 | zWarning = 0; |
| 58 | }else{ |
| 59 | syncFlags = SYNC_PUSH | SYNC_PULL; |
| 60 | zButton = "Synchronize"; |
| 61 | zWarning = mprintf("WARNING: Pushing to \"%s\" is enabled.", |
| 62 | g.urlCanonical); |
| 63 | } |
| 64 | if( P("sync") ){ |
| 65 | user_select(); |
| 66 | url_enable_proxy(0); |
| 67 | client_sync(syncFlags, 0, 0); |
| 68 | } |
| 69 | @ <p>Press the %h(zButton) button below to synchronize with the |
| 70 | @ "%h(g.urlCanonical)" repository now. This may be useful when |
| 71 | @ testing the various transfer scripts.</p> |
| 72 | @ <p>You can use the "http -async" command in your scripts, but |
| 73 | @ make sure the "th1-uri-regexp" setting is set first.</p> |
| 74 | if( zWarning ){ |
| 75 | @ |
| 76 | @ <big><b>%h(zWarning)</b></big> |
| 77 | free(zWarning); |
| 78 | } |
| 79 | @ |
| 80 | @ <blockquote> |
| 81 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 82 | login_insert_csrf_secret(); |
| 83 | @ <input type="submit" name="sync" value="%h(zButton)" /> |
| 84 | @ </div></form> |
| 85 | @ </blockquote> |
| 86 | @ |
| 87 | } |
| 88 | |
| 89 | style_footer(); |
| 90 | } |
| 91 | |
| 92 | /* |
| 93 | ** Common implementation for the transfer setup editor pages. |
| @@ -140,9 +187,49 @@ | |
| 187 | "Transfer Push Script", |
| 188 | "xfer-push-script", |
| 189 | zDefaultXferPush, |
| 190 | zDesc, |
| 191 | 0, |
| 192 | 0, |
| 193 | 30 |
| 194 | ); |
| 195 | } |
| 196 | |
| 197 | static const char *zDefaultXferCommit = 0; |
| 198 | |
| 199 | /* |
| 200 | ** WEBPAGE: xfersetup_commit |
| 201 | */ |
| 202 | void xfersetup_commit_page(void){ |
| 203 | static const char zDesc[] = |
| 204 | @ Enter TH1 script that runs when a commit is processed. |
| 205 | ; |
| 206 | xfersetup_generic( |
| 207 | "Transfer Commit Script", |
| 208 | "xfer-commit-script", |
| 209 | zDefaultXferCommit, |
| 210 | zDesc, |
| 211 | 0, |
| 212 | 0, |
| 213 | 30 |
| 214 | ); |
| 215 | } |
| 216 | |
| 217 | static const char *zDefaultXferTicket = 0; |
| 218 | |
| 219 | /* |
| 220 | ** WEBPAGE: xfersetup_ticket |
| 221 | */ |
| 222 | void xfersetup_ticket_page(void){ |
| 223 | static const char zDesc[] = |
| 224 | @ Enter TH1 script that runs when a ticket change is processed. |
| 225 | ; |
| 226 | xfersetup_generic( |
| 227 | "Transfer Ticket Script", |
| 228 | "xfer-ticket-script", |
| 229 | zDefaultXferTicket, |
| 230 | zDesc, |
| 231 | 0, |
| 232 | 0, |
| 233 | 30 |
| 234 | ); |
| 235 | } |
| 236 |
+20
| --- test/th1.test | ||
| +++ test/th1.test | ||
| @@ -65,5 +65,25 @@ | ||
| 65 | 65 | |
| 66 | 66 | ############################################################################### |
| 67 | 67 | |
| 68 | 68 | fossil test-th-eval --th-open-config "setting -strict -- --" |
| 69 | 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}} | |
| 70 | 90 |
| --- test/th1.test | |
| +++ test/th1.test | |
| @@ -65,5 +65,25 @@ | |
| 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 |
| --- test/th1.test | |
| +++ test/th1.test | |
| @@ -65,5 +65,25 @@ | |
| 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 |
+20
| --- test/th1.test | ||
| +++ test/th1.test | ||
| @@ -65,5 +65,25 @@ | ||
| 65 | 65 | |
| 66 | 66 | ############################################################################### |
| 67 | 67 | |
| 68 | 68 | fossil test-th-eval --th-open-config "setting -strict -- --" |
| 69 | 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}} | |
| 70 | 90 |
| --- test/th1.test | |
| +++ test/th1.test | |
| @@ -65,5 +65,25 @@ | |
| 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 |
| --- test/th1.test | |
| +++ test/th1.test | |
| @@ -65,5 +65,25 @@ | |
| 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 |
+3
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -32,10 +32,13 @@ | ||
| 32 | 32 | which does not store the URL or password when cloning. |
| 33 | 33 | * Modify [/help?cmd=ui | fossil ui] to respect "default user" in an open |
| 34 | 34 | repository. |
| 35 | 35 | * Fossil now hides check-ins that have the "hidden" tag in timeline webpages. |
| 36 | 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 option to "[/help?cmd=commit | fossil commit]" command. | |
| 37 | 40 | |
| 38 | 41 | <h2>Changes For Version 1.27 (2013-09-11)</h2> |
| 39 | 42 | * Enhance the [/help?cmd=changes | fossil changes], |
| 40 | 43 | [/help?cmd=clean | fossil clean], [/help?cmd=extras | fossil extras], |
| 41 | 44 | [/help?cmd=ls | fossil ls] and [/help?cmd=status | fossil status] commands |
| 42 | 45 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -32,10 +32,13 @@ | |
| 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,13 @@ | |
| 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 option to "[/help?cmd=commit | fossil commit]" command. |
| 40 | |
| 41 | <h2>Changes For Version 1.27 (2013-09-11)</h2> |
| 42 | * Enhance the [/help?cmd=changes | fossil changes], |
| 43 | [/help?cmd=clean | fossil clean], [/help?cmd=extras | fossil extras], |
| 44 | [/help?cmd=ls | fossil ls] and [/help?cmd=status | fossil status] commands |
| 45 |