Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | Merge trunk, since cloning local filesystem repositories still does not work |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | multi-remote-fix |
Files: | files | file ages | folders |
SHA3-256: |
719dcd29cd8a99d592793211fe60f6c0 |
User & Date: | andygoth 2020-08-16 14:45:33 |
2020-08-16
| ||
15:30 | Close multi-remote-fix branch because the issue is now resolved. See https://fossil-scm.org/forum/forumpost/158a67cc53 for some information on what was going on. The solution was to reopen the configuration after it was being prematurely closed. ... (check-in: 4c16a249 user: andygoth tags: bad-merge) | |
14:45 | Merge trunk, since cloning local filesystem repositories still does not work ... (Closed-Leaf check-in: 719dcd29 user: andygoth tags: multi-remote-fix) | |
14:31 | Remove "(pending)" from 2.12 changelog now that 2.12 is released ... (check-in: dcc90ab5 user: andygoth tags: trunk) | |
2020-07-30
| ||
00:11 | Revive branch, since it turns out there is still a problem with cloning local filesystem repositories. See the previously-linked forum post for details. ... (check-in: cbd52334 user: andygoth tags: multi-remote-fix) | |
Changes to VERSION.
|
| | | 1 | 2.13 |
Changes to skins/ardoise/css.txt.
︙ | ︙ | |||
495 496 497 498 499 500 501 | color: #bbb; background-color: #303536; border: 0; border-radius: 5px; box-shadow: none; box-sizing: border-box } | | | 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 | color: #bbb; background-color: #303536; border: 0; border-radius: 5px; box-shadow: none; box-sizing: border-box } textarea, select { height: initial; } input[type=email]:hover, input[type=number]:hover, input[type=password]:hover, input[type=search]:hover, input[type=tel]:hover, |
︙ | ︙ | |||
570 571 572 573 574 575 576 577 578 579 580 581 582 583 | margin: 0 .2rem; font-size: 90%; white-space: nowrap; background: #000; border: 2px solid #bbb; border-radius: 5px } pre > code { padding: 1rem 1.5rem; white-space: pre } td, th { padding: 1px 5px; | > > > | 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 | margin: 0 .2rem; font-size: 90%; white-space: nowrap; background: #000; border: 2px solid #bbb; border-radius: 5px } table.numbered-lines td.file-content > pre { margin-top: -2px/*offset CODE tag border*/; } pre > code { padding: 1rem 1.5rem; white-space: pre } td, th { padding: 1px 5px; |
︙ | ︙ |
Changes to skins/eagle/css.txt.
︙ | ︙ | |||
397 398 399 400 401 402 403 | font-family: "courier new"; } div.filetreeline:hover { background-color: #7EA2D9; } | | | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 | font-family: "courier new"; } div.filetreeline:hover { background-color: #7EA2D9; } table.numbered-lines td.line-numbers span.selected-line { background-color: #7EA2D9; } .statistics-report-graph-line { background-color: #7EA2D9; } |
︙ | ︙ |
Changes to skins/xekri/css.txt.
︙ | ︙ | |||
998 999 1000 1001 1002 1003 1004 | /************************************** * Did not encounter these */ /* selected lines of text within a linenumbered artifact display */ | | | | 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 | /************************************** * Did not encounter these */ /* selected lines of text within a linenumbered artifact display */ table.numbered-lines td.line-numbers span.selected-line { font-weight: bold; color: #00f; background-color: #d5d5ff; border-color: #00f; } /* format for missing privileges note on user setup page */ p.missingPriv { color: #00f; } |
︙ | ︙ |
Changes to src/add.c.
︙ | ︙ | |||
318 319 320 321 322 323 324 | /* ** COMMAND: add ** ** Usage: %fossil add ?OPTIONS? FILE1 ?FILE2 ...? ** ** Make arrangements to add one or more files or directories to the | | | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | /* ** COMMAND: add ** ** Usage: %fossil add ?OPTIONS? FILE1 ?FILE2 ...? ** ** Make arrangements to add one or more files or directories to the ** current checkout at the next [[commit]]. ** ** When adding files or directories recursively, filenames that begin ** with "." are excluded by default. To include such files, add ** the "--dotfiles" option to the command-line. ** ** The --ignore and --clean options are comma-separated lists of glob patterns ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore |
︙ | ︙ | |||
357 358 359 360 361 362 363 | ** than --verbose and --dry-run may be used ** with --reset. ** ** The following options are only valid with --reset: ** -v|--verbose Outputs information about each --reset file. ** -n|--dry-run Display instead of run actions. ** | | | 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | ** than --verbose and --dry-run may be used ** with --reset. ** ** The following options are only valid with --reset: ** -v|--verbose Outputs information about each --reset file. ** -n|--dry-run Display instead of run actions. ** ** See also: [[addremove]], [[rm]] */ void add_cmd(void){ int i; /* Loop counter */ int vid; /* Currently checked out version */ int nRoot; /* Full path characters in g.zLocalRoot */ const char *zCleanFlag; /* The --clean option or clean-glob setting */ const char *zIgnoreFlag; /* The --ignore option or ignore-glob setting */ |
︙ | ︙ | |||
539 540 541 542 543 544 545 | ** that all newly-rm'd (but not yet committed) ** files are no longer removed. No flags other ** than --verbose or --dry-run may be used with ** --reset. ** --verbose|-v Outputs information about each --reset file. ** Only usable with --reset. ** | | | 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 | ** that all newly-rm'd (but not yet committed) ** files are no longer removed. No flags other ** than --verbose or --dry-run may be used with ** --reset. ** --verbose|-v Outputs information about each --reset file. ** Only usable with --reset. ** ** See also: [[addremove]], [[add]] */ void delete_cmd(void){ int i; int removeFiles; int dryRunFlag = find_option("dry-run","n",0)!=0; int softFlag; int hardFlag; |
︙ | ︙ | |||
687 688 689 690 691 692 693 | } /* ** COMMAND: addremove ** ** Usage: %fossil addremove ?OPTIONS? ** | | | | | | | 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 | } /* ** COMMAND: addremove ** ** Usage: %fossil addremove ?OPTIONS? ** ** Do all necessary "[[add]]" and "[[rm]]" commands to synchronize the ** repository with the content of the working checkout: ** ** * All files in the checkout but not in the repository (that is, ** all files displayed using the "extras" command) are added as ** if by the "[[add]]" command. ** ** * All files in the repository but missing from the checkout (that is, ** all files that show as MISSING with the "status" command) are ** removed as if by the "[[rm]]" command. ** ** The command does not "[[commit]]". You must run the "[[commit]]" separately ** as a separate step. ** ** Files and directories whose names begin with "." are ignored unless ** the --dotfiles option is used. ** ** The --ignore option overrides the "ignore-glob" setting, as do the ** --case-sensitive option with the "case-sensitive" setting and the |
︙ | ︙ | |||
731 732 733 734 735 736 737 | ** files are no longer added and all newly-removed ** (but not yet committed) files are no longer ** removed. No flags other than --verbose and ** --dry-run may be used with --reset. ** --verbose|-v Outputs information about each --reset file. ** Only usable with --reset. ** | | | 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 | ** files are no longer added and all newly-removed ** (but not yet committed) files are no longer ** removed. No flags other than --verbose and ** --dry-run may be used with --reset. ** --verbose|-v Outputs information about each --reset file. ** Only usable with --reset. ** ** See also: [[add]], [[rm]] */ void addremove_cmd(void){ Blob path; const char *zCleanFlag; const char *zIgnoreFlag; unsigned scanFlags; int dryRunFlag = find_option("dry-run","n",0)!=0; |
︙ | ︙ | |||
951 952 953 954 955 956 957 | ** or: %fossil mv|rename OLDNAME... DIR ** ** Move or rename one or more files or directories within the repository tree. ** You can either rename a file or directory or move it to another subdirectory. ** ** The 'mv' command does NOT normally rename or move the files on disk. ** This command merely records the fact that file names have changed so | | | | | | | | | 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 | ** or: %fossil mv|rename OLDNAME... DIR ** ** Move or rename one or more files or directories within the repository tree. ** You can either rename a file or directory or move it to another subdirectory. ** ** The 'mv' command does NOT normally rename or move the files on disk. ** This command merely records the fact that file names have changed so ** that appropriate notations can be made at the next [[commit]]. ** However, the default behavior of this command may be overridden via ** command line options listed below and/or the 'mv-rm-files' setting. ** ** The 'rename' command never renames or moves files on disk, even when the ** command line options and/or the 'mv-rm-files' setting would otherwise ** require it to do so. ** ** WARNING: If the "--hard" option is specified -OR- the "mv-rm-files" ** setting is non-zero, files WILL BE renamed or moved on disk ** as well. This does NOT apply to the 'rename' command. ** ** Options: ** --soft Skip moving files within the checkout. ** This supersedes the --hard option. ** --hard Move files within the checkout. ** --case-sensitive <BOOL> Override the case-sensitive setting. ** -n|--dry-run If given, display instead of run actions. ** ** See also: [[changes]], [[status]] */ void mv_cmd(void){ int i; int vid; int moveFiles; int dryRunFlag; int softFlag; |
︙ | ︙ |
Changes to src/ajax.c.
︙ | ︙ | |||
128 129 130 131 132 133 134 | case AJAX_RENDER_WIKI: safe_html_context(DOCSRC_FILE); wiki_render_by_mimetype(pContent, zMime); break; default:{ const char *zContent = blob_str(pContent); if(AJAX_PREVIEW_LINE_NUMBERS & flags){ | | > | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | case AJAX_RENDER_WIKI: safe_html_context(DOCSRC_FILE); wiki_render_by_mimetype(pContent, zMime); break; default:{ const char *zContent = blob_str(pContent); if(AJAX_PREVIEW_LINE_NUMBERS & flags){ output_text_with_line_numbers(zContent, blob_size(pContent), zName, "on"); }else{ const char *zExt = strrchr(zName,'.'); if(zExt && zExt[1]){ CX("<pre><code class='language-%s'>%h</code></pre>", zExt+1, zContent); }else{ CX("<pre>%h</pre>", zContent); |
︙ | ︙ | |||
161 162 163 164 165 166 167 168 169 170 171 172 173 174 | }else if(DIFF_SIDEBYSIDE & diffFlags){ CX("%b",&out); }else{ CX("<pre class='udiff'>%b</pre>",&out); } blob_reset(&out); } /* ** Helper for /ajax routes. Clears the CGI content buffer, sets an ** HTTP error status code, and queues up a JSON response in the form ** of an object: ** ** {error: formatted message} | > > > > > > > > > > | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | }else if(DIFF_SIDEBYSIDE & diffFlags){ CX("%b",&out); }else{ CX("<pre class='udiff'>%b</pre>",&out); } blob_reset(&out); } /* ** Uses P(zKey) to fetch a CGI environment variable. If that var is ** NULL or starts with '0' or 'f' then this function returns false, ** else it returns true. */ int ajax_p_bool(char const *zKey){ const char * zVal = P(zKey); return (!zVal || '0'==*zVal || 'f'==*zVal) ? 0 : 1; } /* ** Helper for /ajax routes. Clears the CGI content buffer, sets an ** HTTP error status code, and queues up a JSON response in the form ** of an object: ** ** {error: formatted message} |
︙ | ︙ | |||
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | assert(!"cannot happen"); } if(zRenderMode!=0){ cgi_printf_header("x-ajax-render-mode: %s\r\n", zRenderMode); } } /* ** Internal mapping of ajax sub-route names to various metadata. */ struct AjaxRoute { const char *zName; /* Name part of the route after "ajax/" */ void (*xCallback)(); /* Impl function for the route. */ int bWriteMode; /* True if requires write mode */ int bPost; /* True if requires POST (i.e. CSRF ** verification) */ }; typedef struct AjaxRoute AjaxRoute; /* ** Comparison function for bsearch() for searching an AjaxRoute ** list for a matching name. */ | > > | | 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | assert(!"cannot happen"); } if(zRenderMode!=0){ cgi_printf_header("x-ajax-render-mode: %s\r\n", zRenderMode); } } #if INTERFACE /* ** Internal mapping of ajax sub-route names to various metadata. */ struct AjaxRoute { const char *zName; /* Name part of the route after "ajax/" */ void (*xCallback)(); /* Impl function for the route. */ int bWriteMode; /* True if requires write mode */ int bPost; /* True if requires POST (i.e. CSRF ** verification) */ }; typedef struct AjaxRoute AjaxRoute; #endif /*INTERFACE*/ /* ** Comparison function for bsearch() for searching an AjaxRoute ** list for a matching name. */ int cmp_ajax_route_name(const void *a, const void *b){ const AjaxRoute * rA = (const AjaxRoute*)a; const AjaxRoute * rB = (const AjaxRoute*)b; return fossil_strcmp(rA->zName, rB->zName); } /* ** WEBPAGE: ajax |
︙ | ︙ |
Changes to src/allrepo.c.
︙ | ︙ | |||
217 218 219 220 221 222 223 224 225 226 227 228 229 230 | } }else if( strncmp(zCmd, "dbstat", n)==0 ){ zCmd = "dbstat --omit-version-info -R"; showLabel = 1; quiet = 1; collect_argument(&extra, "brief", "b"); collect_argument(&extra, "db-check", 0); }else if( strncmp(zCmd, "extras", n)==0 ){ if( showFile ){ zCmd = "extras --chdir"; }else{ zCmd = "extras --header --chdir"; } collect_argument(&extra, "abs-paths",0); | > | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | } }else if( strncmp(zCmd, "dbstat", n)==0 ){ zCmd = "dbstat --omit-version-info -R"; showLabel = 1; quiet = 1; collect_argument(&extra, "brief", "b"); collect_argument(&extra, "db-check", 0); collect_argument(&extra, "db-verify", 0); }else if( strncmp(zCmd, "extras", n)==0 ){ if( showFile ){ zCmd = "extras --chdir"; }else{ zCmd = "extras --header --chdir"; } collect_argument(&extra, "abs-paths",0); |
︙ | ︙ |
Changes to src/attach.c.
︙ | ︙ | |||
615 616 617 618 619 620 621 | blob_zero(&attach); if( fShowContent ){ const char *z; content_get(ridSrc, &attach); blob_to_utf8_no_bom(&attach, 0); z = blob_str(&attach); if( zLn ){ | | | 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 | blob_zero(&attach); if( fShowContent ){ const char *z; content_get(ridSrc, &attach); blob_to_utf8_no_bom(&attach, 0); z = blob_str(&attach); if( zLn ){ output_text_with_line_numbers(z, blob_size(&attach), zName, zLn); }else{ @ <pre> @ %h(z) @ </pre> } }else if( strncmp(zMime, "image/", 6)==0 ){ int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); |
︙ | ︙ |
Changes to src/bisect.c.
︙ | ︙ | |||
379 380 381 382 383 384 385 | } /* ** COMMAND: bisect ** ** Usage: %fossil bisect SUBCOMMAND ... ** | | > | 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 | } /* ** COMMAND: bisect ** ** Usage: %fossil bisect SUBCOMMAND ... ** ** Run various subcommands useful for searching back through the change ** history for a particular checkin that causes or fixes a problem. ** ** > fossil bisect bad ?VERSION? ** ** Identify version VERSION as non-working. If VERSION is omitted, ** the current checkout is marked as non-working. ** ** > fossil bisect good ?VERSION? |
︙ | ︙ |
Changes to src/blob.c.
︙ | ︙ | |||
485 486 487 488 489 490 491 492 493 494 495 496 497 498 | ** allocated to it. Does not modify pBlob->nUsed nor will it reduce ** the currently-allocated amount of memory. ** ** For semantic compatibility with blob_append_full(), if newSize is ** >=0x7fff000 (~2GB) then this function will trigger blob_panic(). If ** it didn't, it would be possible to bypass that hard-coded limit via ** this function. */ void blob_reserve(Blob *pBlob, unsigned int newSize){ if(newSize>=0x7fff0000 ){ blob_panic(); }else if(newSize>pBlob->nUsed){ pBlob->xRealloc(pBlob, newSize); pBlob->aData[newSize] = 0; | > > > > > | 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 | ** allocated to it. Does not modify pBlob->nUsed nor will it reduce ** the currently-allocated amount of memory. ** ** For semantic compatibility with blob_append_full(), if newSize is ** >=0x7fff000 (~2GB) then this function will trigger blob_panic(). If ** it didn't, it would be possible to bypass that hard-coded limit via ** this function. ** ** We've had at least one report: ** https://fossil-scm.org/forum/forumpost/b7bbd28db4 ** which implies that this is unconditionally failing on mingw 32-bit ** builds. */ void blob_reserve(Blob *pBlob, unsigned int newSize){ if(newSize>=0x7fff0000 ){ blob_panic(); }else if(newSize>pBlob->nUsed){ pBlob->xRealloc(pBlob, newSize); pBlob->aData[newSize] = 0; |
︙ | ︙ |
Changes to src/branch.c.
︙ | ︙ | |||
380 381 382 383 384 385 386 | ** replaced by a space, and it may also name a timezone offset ** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward). ** Either no timezone suffix or "Z" means UTC. ** ** Options valid for all subcommands: ** ** -R|--repository FILE Run commands on repository FILE | < < < < < < | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 | ** replaced by a space, and it may also name a timezone offset ** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward). ** Either no timezone suffix or "Z" means UTC. ** ** Options valid for all subcommands: ** ** -R|--repository FILE Run commands on repository FILE */ void branch_cmd(void){ int n; const char *zCmd = "list"; db_find_and_open_repository(0, 0); if( g.argc>=3 ) zCmd = g.argv[2]; n = strlen(zCmd); |
︙ | ︙ |
Changes to src/browse.c.
︙ | ︙ | |||
909 910 911 912 913 914 915 | while( nClose-- > 0 ){ @ </ul> } } } @ </ul> @ </ul></div> | | | 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 | while( nClose-- > 0 ){ @ </ul> } } } @ </ul> @ </ul></div> builtin_request_js("tree.js"); style_footer(); /* We could free memory used by sTree here if we needed to. But ** the process is about to exit, so doing so would not really accomplish ** anything useful. */ } |
︙ | ︙ |
Changes to src/builtin.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 | ** The resources provided by this file are packaged by the "mkbuiltin.c" ** utility program during the built process and stored in the ** builtin_data.h file. Include that information here: */ #include "builtin_data.h" /* | | > > | | > > | | | > > > > > > > > > | | > | | | > > | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | ** The resources provided by this file are packaged by the "mkbuiltin.c" ** utility program during the built process and stored in the ** builtin_data.h file. Include that information here: */ #include "builtin_data.h" /* ** Return the index in the aBuiltinFiles[] array for the file ** whose name is zFilename. Or return -1 if the file is not ** found. */ static int builtin_file_index(const char *zFilename){ int lwr, upr, i, c; lwr = 0; upr = count(aBuiltinFiles) - 1; while( upr>=lwr ){ i = (upr+lwr)/2; c = strcmp(aBuiltinFiles[i].zName,zFilename); if( c<0 ){ lwr = i+1; }else if( c>0 ){ upr = i-1; }else{ return i; } } return -1; } /* ** Return a pointer to built-in content */ const unsigned char *builtin_file(const char *zFilename, int *piSize){ int i = builtin_file_index(zFilename); if( i>=0 ){ if( piSize ) *piSize = aBuiltinFiles[i].nByte; return aBuiltinFiles[i].pData; }else{ if( piSize ) *piSize = 0; return 0; } } const char *builtin_text(const char *zFilename){ return (char*)builtin_file(zFilename, 0); } /* ** COMMAND: test-builtin-list ** ** If -verbose is used, it outputs a line at the end ** with the total item count and size. ** ** List the names and sizes of all built-in resources. */ void test_builtin_list(void){ int i, size = 0;; for(i=0; i<count(aBuiltinFiles); i++){ const int n = aBuiltinFiles[i].nByte; fossil_print("%3d. %-45s %6d\n", i+1, aBuiltinFiles[i].zName,n); size += n; } if(find_option("verbose","v",0)!=0){ fossil_print("%d entries totaling %d bytes\n", i, size); } } /* ** WEBPAGE: test-builtin-files ** ** Show all built-in text files. */ void test_builtin_list_page(void){ int i; style_header("Built-in Text Files"); @ <ol> for(i=0; i<count(aBuiltinFiles); i++){ const char *z = aBuiltinFiles[i].zName; char *zUrl = href("%R/builtin?name=%T&id=%.8s&mimetype=text/plain", z,fossil_exe_id()); @ <li>%z(zUrl)%h(z)</a> } @ </ol> style_footer(); } /* ** COMMAND: test-builtin-get ** ** Usage: %fossil test-builtin-get NAME ?OUTPUT-FILE? |
︙ | ︙ | |||
108 109 110 111 112 113 114 | if( pData==0 ){ fossil_fatal("no such built-in file: [%s]", g.argv[2]); } blob_init(&x, (const char*)pData, nByte); blob_write_to_file(&x, g.argc==4 ? g.argv[3] : "-"); blob_reset(&x); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 | if( pData==0 ){ fossil_fatal("no such built-in file: [%s]", g.argv[2]); } blob_init(&x, (const char*)pData, nByte); blob_write_to_file(&x, g.argc==4 ? g.argv[3] : "-"); blob_reset(&x); } /* ** Input zList is a list of numeric identifiers for files in ** aBuiltinFiles[]. Return the concatenation of all of those ** files using mimetype zType, or as application/javascript if ** zType is 0. */ static void builtin_deliver_multiple_js_files( const char *zList, /* List of numeric identifiers */ const char *zType /* Override mimetype */ ){ Blob *pOut; if( zType==0 ) zType = "application/javascript"; cgi_set_content_type(zType); pOut = cgi_output_blob(); while( zList[0] ){ int i = atoi(zList); if( i>0 && i<=count(aBuiltinFiles) ){ blob_appendf(pOut, "/* %s */\n", aBuiltinFiles[i-1].zName); blob_append(pOut, (const char*)aBuiltinFiles[i-1].pData, aBuiltinFiles[i-1].nByte); } while( fossil_isdigit(zList[0]) ) zList++; if( zList[0]==',' ) zList++; } return; } /* ** WEBPAGE: builtin ** ** Return one of many built-in content files. Query parameters: ** ** name=FILENAME Return the single file whose name is FILENAME. ** mimetype=TYPE Override the mimetype in the returned file to ** be TYPE. If this query parameter is omitted ** (the usual case) then the mimetype is inferred ** from the suffix on FILENAME ** m=IDLIST IDLIST is a comma-separated list of integers ** that specify multiple javascript files to be ** concatenated and returned all at once. ** id=UNIQUEID Version number of the "builtin" files. Used ** for cache control only. ** ** At least one of the name= or m= query parameters must be present. ** ** If the id= query parameter is present, then Fossil assumes that the ** result is immutable and sets a very large cache retention time (1 year). */ void builtin_webpage(void){ Blob out; const char *zName = P("name"); const char *zTxt = 0; const char *zId = P("id"); const char *zType = P("mimetype"); int nId; if( zName ) zTxt = builtin_text(zName); if( zTxt==0 ){ const char *zM = P("m"); if( zM ){ if( zId && (nId = (int)strlen(zId))>=8 && strncmp(zId,fossil_exe_id(),nId)==0 ){ g.isConst = 1; } etag_check(0,0); builtin_deliver_multiple_js_files(zM, zType); return; } cgi_set_status(404, "Not Found"); @ File "%h(zName)" not found return; } if( zType==0 ){ if( sqlite3_strglob("*.js", zName)==0 ){ zType = "application/javascript"; }else{ zType = mimetype_from_name(zName); } } cgi_set_content_type(zType); if( zId && (nId = (int)strlen(zId))>=8 && strncmp(zId,fossil_exe_id(),nId)==0 ){ g.isConst = 1; } etag_check(0,0); blob_init(&out, zTxt, -1); cgi_set_content(&out); } /* Variables controlling the JS cache. */ static struct { int aReq[30]; /* Indexes of all requested built-in JS files */ int nReq; /* Number of slots in aReq[] currently used */ int nSent; /* Number of slots in aReq[] fulfilled */ int eDelivery; /* Delivery mechanism */ } builtin; #if INTERFACE /* Various delivery mechanisms. The 0 option is the default. */ #define JS_INLINE 0 /* inline, batched together at end of file */ #define JS_SEPARATE 1 /* Separate HTTP request for each JS file */ #define JS_BUNDLED 2 /* One HTTP request to load all JS files */ /* concatenated together into a bundle */ #endif /* INTERFACE */ /* ** The argument is a request to change the javascript delivery mode. ** The argument is a string which is a command-line option or CGI ** parameter. Try to match it against one of the delivery options ** and set things up accordingly. Throw an error if no match unless ** bSilent is true. */ void builtin_set_js_delivery_mode(const char *zMode, int bSilent){ if( zMode==0 ) return; if( strcmp(zMode, "inline")==0 ){ builtin.eDelivery = JS_INLINE; }else if( strcmp(zMode, "separate")==0 ){ builtin.eDelivery = JS_SEPARATE; }else if( strcmp(zMode, "bundled")==0 ){ builtin.eDelivery = JS_BUNDLED; }else if( !bSilent ){ fossil_fatal("unknown javascript delivery mode \"%s\" - should be" " one of: inline separate bundled", zMode); } } /* ** The caller wants the Javascript file named by zFilename to be ** included in the generated page. Add the file to the queue of ** requested javascript resources, if it is not there already. ** ** The current implementation queues the file to be included in the ** output later. However, the caller should not depend on that ** behavior. In the future, this routine might decide to insert ** the requested javascript inline, immedaitely, or to insert ** a <script src=..> element to reference the javascript as a ** separate resource. The exact behavior might change in the future ** so pages that use this interface must not rely on any particular ** behavior. ** ** All this routine guarantees is that the named javascript file ** will be requested by the browser at some point. This routine ** does not guarantee when the javascript will be included, and it ** does not guarantee whether the javascript will be added inline or ** delivered as a separate resource. */ void builtin_request_js(const char *zFilename){ int i = builtin_file_index(zFilename); int j; if( i<0 ){ fossil_panic("unknown javascript file: \"%s\"", zFilename); } for(j=0; j<builtin.nReq; j++){ if( builtin.aReq[j]==i ) return; /* Already queued or sent */ } if( builtin.nReq>=count(builtin.aReq) ){ fossil_panic("too many javascript files requested"); } builtin.aReq[builtin.nReq++] = i; } /* ** Fulfill all pending requests for javascript files. ** ** The current implementation delivers all javascript in-line. However, ** the caller should not depend on this. Future changes to this routine ** might choose to deliver javascript as separate resources. */ void builtin_fulfill_js_requests(void){ if( builtin.nSent>=builtin.nReq ) return; /* nothing to do */ switch( builtin.eDelivery ){ case JS_INLINE: { CX("<script nonce='%h'>\n",style_nonce()); do{ int i = builtin.aReq[builtin.nSent++]; CX("/* %s */\n", aBuiltinFiles[i].zName); cgi_append_content((const char*)aBuiltinFiles[i].pData, aBuiltinFiles[i].nByte); }while( builtin.nSent<builtin.nReq ); CX("</script>\n"); break; } case JS_BUNDLED: { if( builtin.nSent+1<builtin.nReq ){ Blob aList; blob_init(&aList,0,0); while( builtin.nSent<builtin.nReq ){ blob_appendf(&aList, ",%d", builtin.aReq[builtin.nSent++]+1); } CX("<script src='%R/builtin?m=%s&id=%.8s'></script>\n", blob_str(&aList)+1, fossil_exe_id()); blob_reset(&aList); break; } /* If there is only one JS file, fall through into the ** JS_SEPARATE case below. */ /*FALLTHROUGH*/ } case JS_SEPARATE: { /* Each JS file as a separate resource */ while( builtin.nSent<builtin.nReq ){ int i = builtin.aReq[builtin.nSent++]; CX("<script src='%R/builtin?name=%t&id=%.8s'></script>\n", aBuiltinFiles[i].zName, fossil_exe_id()); } break; } } } /***************************************************************************** ** A virtual table for accessing the information in aBuiltinFiles[]. */ /* builtinVtab_vtab is a subclass of sqlite3_vtab which is ** underlying representation of the virtual table */ typedef struct builtinVtab_vtab builtinVtab_vtab; struct builtinVtab_vtab { sqlite3_vtab base; /* Base class - must be first */ /* Add new fields here, as necessary */ }; /* builtinVtab_cursor is a subclass of sqlite3_vtab_cursor which will ** serve as the underlying representation of a cursor that scans ** over rows of the result */ typedef struct builtinVtab_cursor builtinVtab_cursor; struct builtinVtab_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ /* Insert new fields here. For this builtinVtab we only keep track ** of the rowid */ sqlite3_int64 iRowid; /* The rowid */ }; /* ** The builtinVtabConnect() method is invoked to create a new ** builtin virtual table. ** ** Think of this routine as the constructor for builtinVtab_vtab objects. ** ** All this routine needs to do is: ** ** (1) Allocate the builtinVtab_vtab object and initialize all fields. ** ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the ** result set of queries against the virtual table will look like. */ static int builtinVtabConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ builtinVtab_vtab *pNew; int rc; rc = sqlite3_declare_vtab(db, "CREATE TABLE x(name,size,data)" ); if( rc==SQLITE_OK ){ pNew = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); } return rc; } /* ** This method is the destructor for builtinVtab_vtab objects. */ static int builtinVtabDisconnect(sqlite3_vtab *pVtab){ builtinVtab_vtab *p = (builtinVtab_vtab*)pVtab; sqlite3_free(p); return SQLITE_OK; } /* ** Constructor for a new builtinVtab_cursor object. */ static int builtinVtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ builtinVtab_cursor *pCur; pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); *ppCursor = &pCur->base; return SQLITE_OK; } /* ** Destructor for a builtinVtab_cursor. */ static int builtinVtabClose(sqlite3_vtab_cursor *cur){ builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; sqlite3_free(pCur); return SQLITE_OK; } /* ** Advance a builtinVtab_cursor to its next row of output. */ static int builtinVtabNext(sqlite3_vtab_cursor *cur){ builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; pCur->iRowid++; return SQLITE_OK; } /* ** Return values of columns for the row at which the builtinVtab_cursor ** is currently pointing. */ static int builtinVtabColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; const struct BuiltinFileTable *pFile = aBuiltinFiles + pCur->iRowid; switch( i ){ case 0: /* name */ sqlite3_result_text(ctx, pFile->zName, -1, SQLITE_STATIC); break; case 1: /* size */ sqlite3_result_int(ctx, pFile->nByte); break; case 2: /* data */ sqlite3_result_blob(ctx, pFile->pData, pFile->nByte, SQLITE_STATIC); break; } return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** rowid is the same as the output value. */ static int builtinVtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; *pRowid = pCur->iRowid; return SQLITE_OK; } /* ** Return TRUE if the cursor has been moved off of the last ** row of output. */ static int builtinVtabEof(sqlite3_vtab_cursor *cur){ builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; return pCur->iRowid>=count(aBuiltinFiles); } /* ** This method is called to "rewind" the builtinVtab_cursor object back ** to the first row of output. This method is always called at least ** once prior to any call to builtinVtabColumn() or builtinVtabRowid() or ** builtinVtabEof(). */ static int builtinVtabFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ builtinVtab_cursor *pCur = (builtinVtab_cursor *)pVtabCursor; pCur->iRowid = 1; return SQLITE_OK; } /* ** SQLite will invoke this method one or more times while planning a query ** that uses the virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that ** plan. */ static int builtinVtabBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ pIdxInfo->estimatedCost = (double)count(aBuiltinFiles); pIdxInfo->estimatedRows = count(aBuiltinFiles); return SQLITE_OK; } /* ** This following structure defines all the methods for the ** virtual table. */ static sqlite3_module builtinVtabModule = { /* iVersion */ 0, /* xCreate */ 0, /* The builtin vtab is eponymous and read-only */ /* xConnect */ builtinVtabConnect, /* xBestIndex */ builtinVtabBestIndex, /* xDisconnect */ builtinVtabDisconnect, /* xDestroy */ 0, /* xOpen */ builtinVtabOpen, /* xClose */ builtinVtabClose, /* xFilter */ builtinVtabFilter, /* xNext */ builtinVtabNext, /* xEof */ builtinVtabEof, /* xColumn */ builtinVtabColumn, /* xRowid */ builtinVtabRowid, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0, /* xShadowName */ 0 }; /* ** Register the builtin virtual table */ int builtin_vtab_register(sqlite3 *db){ int rc = sqlite3_create_module(db, "builtin", &builtinVtabModule, 0); return rc; } /* End of the builtin virtual table ******************************************************************************/ |
Changes to src/bundle.c.
︙ | ︙ | |||
714 715 716 717 718 719 720 | }else{ purge_artifact_list("ok",0,0); } db_end_transaction(0); } /* | | | 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 | }else{ purge_artifact_list("ok",0,0); } db_end_transaction(0); } /* ** COMMAND: bundle* ** ** Usage: %fossil bundle SUBCOMMAND ARGS... ** ** > fossil bundle append BUNDLE FILE... ** ** Add files named on the command line to BUNDLE. This subcommand has ** little practical use and is mostly intended for testing. |
︙ | ︙ | |||
766 767 768 769 770 771 772 | ** ** > fossil bundle purge BUNDLE ** ** Remove from the repository all files that are used exclusively ** by check-ins in BUNDLE. This has the effect of undoing a ** "fossil bundle import". ** | < < < < < < < < < < < < < < | | 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 | ** ** > fossil bundle purge BUNDLE ** ** Remove from the repository all files that are used exclusively ** by check-ins in BUNDLE. This has the effect of undoing a ** "fossil bundle import". ** ** See also: [[publish]] */ void bundle_cmd(void){ const char *zSubcmd; int n; if( g.argc<4 ) usage("SUBCOMMAND BUNDLE ?OPTIONS?"); zSubcmd = g.argv[2]; db_find_and_open_repository(0,0); |
︙ | ︙ |
Changes to src/cgi.c.
︙ | ︙ | |||
301 302 303 304 305 306 307 | fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); fprintf(g.httpOut, "Connection: close\r\n"); fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); }else{ assert( rangeEnd==0 ); fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); } | > > > > > > > | < < < < < < < | 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); fprintf(g.httpOut, "Connection: close\r\n"); fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); }else{ assert( rangeEnd==0 ); fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); } if( etag_tag()[0]!=0 ){ fprintf(g.httpOut, "ETag: %s\r\n", etag_tag()); fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage()); if( etag_mtime()>0 ){ fprintf(g.httpOut, "Last-Modified: %s\r\n", cgi_rfc822_datestamp(etag_mtime())); } }else if( g.isConst ){ /* isConst means that the reply is guaranteed to be invariant, even ** after configuration changes and/or Fossil binary recompiles. */ fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n"); }else{ fprintf(g.httpOut, "Cache-control: no-cache\r\n"); } if( blob_size(&extraHeader)>0 ){ fprintf(g.httpOut, "%s", blob_buffer(&extraHeader)); } /* Add headers to turn on useful security options in browsers. */ fprintf(g.httpOut, "X-Frame-Options: SAMEORIGIN\r\n"); |
︙ | ︙ |
Changes to src/checkin.c.
︙ | ︙ | |||
438 439 440 441 442 443 444 | ** --unchanged Display unchanged files. ** --all Display all managed files, i.e. all of the above. ** --extra Display unmanaged files. ** --differ Display modified and extra files. ** --merge Display merge contributors. ** --no-merge Do not display merge contributors. ** | | | 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 | ** --unchanged Display unchanged files. ** --all Display all managed files, i.e. all of the above. ** --extra Display unmanaged files. ** --differ Display modified and extra files. ** --merge Display merge contributors. ** --no-merge Do not display merge contributors. ** ** See also: [[extras]], [[ls]] */ void status_cmd(void){ /* Affirmative and negative flag option tables. */ static const struct { const char *option; /* Flag name. */ unsigned mask; /* Flag bits. */ } flagDefs[] = { |
︙ | ︙ | |||
674 675 676 677 678 679 680 | ** Options: ** --age Show when each file was committed. ** -v|--verbose Provide extra information about each file. ** -t Sort output in time order. ** -r VERSION The specific check-in to list. ** -R|--repository FILE Extract info from repository FILE. ** | | | 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 | ** Options: ** --age Show when each file was committed. ** -v|--verbose Provide extra information about each file. ** -t Sort output in time order. ** -r VERSION The specific check-in to list. ** -R|--repository FILE Extract info from repository FILE. ** ** See also: [[changes]], [[extras]], [[status]] */ void ls_cmd(void){ int vid; Stmt q; int verboseFlag; int showAge; int timeOrder; |
︙ | ︙ | |||
828 829 830 831 832 833 834 | ** --case-sensitive BOOL Override case-sensitive setting ** --dotfiles Include files beginning with a dot (".") ** --header Identify the repository if there are extras ** --ignore CSG Ignore files matching patterns from the argument ** --rel-paths Display pathnames relative to the current working ** directory. ** | | | 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 | ** --case-sensitive BOOL Override case-sensitive setting ** --dotfiles Include files beginning with a dot (".") ** --header Identify the repository if there are extras ** --ignore CSG Ignore files matching patterns from the argument ** --rel-paths Display pathnames relative to the current working ** directory. ** ** See also: [[changes]], [[clean]], [[status]] */ void extras_cmd(void){ Blob report = BLOB_INITIALIZER; const char *zIgnoreFlag = find_option("ignore",0,1); unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0; unsigned flags = C_EXTRA; int showHdr = find_option("header",0,0)!=0; |
︙ | ︙ | |||
952 953 954 955 956 957 958 | ** -n|--dry-run Delete nothing, but display what would have been ** deleted. ** --no-prompt This option disables prompting the user for input ** and assumes an answer of 'No' for every question. ** --temp Remove only Fossil-generated temporary files. ** -v|--verbose Show all files as they are removed. ** | | | 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 | ** -n|--dry-run Delete nothing, but display what would have been ** deleted. ** --no-prompt This option disables prompting the user for input ** and assumes an answer of 'No' for every question. ** --temp Remove only Fossil-generated temporary files. ** -v|--verbose Show all files as they are removed. ** ** See also: [[addremove]], [[extras]], [[status]] */ void clean_cmd(void){ int allFileFlag, allDirFlag, dryRunFlag, verboseFlag; int emptyDirsFlag, dirsOnlyFlag; int disableUndo, noPrompt; int alwaysPrompt = 0; unsigned scanFlags = 0; |
︙ | ︙ | |||
2089 2090 2091 2092 2093 2094 2095 | ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be replaced by ** a space, and it may also name a timezone offset from UTC as "-HH:MM" ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" ** means UTC. ** | | | 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 | ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be replaced by ** a space, and it may also name a timezone offset from UTC as "-HH:MM" ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" ** means UTC. ** ** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]] */ void commit_cmd(void){ int hasChanges; /* True if unsaved changes exist */ int vid; /* blob-id of parent version */ int nrid; /* blob-id of a modified file */ int nvid; /* Blob-id of the new check-in */ Blob comment; /* Check-in comment */ |
︙ | ︙ |
Changes to src/checkout.c.
︙ | ︙ | |||
277 278 279 280 281 282 283 | ** --force Ignore edited files in the current checkout ** --keep Only update the manifest and manifest.uuid files ** --force-missing Force checkout even if content is missing ** --setmtime Set timestamps of all files to match their SCM-side ** times (the timestamp of the last checkin which modified ** them). ** | | | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | ** --force Ignore edited files in the current checkout ** --keep Only update the manifest and manifest.uuid files ** --force-missing Force checkout even if content is missing ** --setmtime Set timestamps of all files to match their SCM-side ** times (the timestamp of the last checkin which modified ** them). ** ** See also: [[update]] */ void checkout_cmd(void){ int forceFlag; /* Force checkout even if edits exist */ int forceMissingFlag; /* Force checkout even if missing content */ int keepFlag; /* Do not change any files on disk */ int latestFlag; /* Checkout the latest version */ char *zVers; /* Version to checkout */ |
︙ | ︙ | |||
385 386 387 388 389 390 391 | } /* ** COMMAND: close* ** ** Usage: %fossil close ?OPTIONS? ** | | | | 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 | } /* ** COMMAND: close* ** ** Usage: %fossil close ?OPTIONS? ** ** The opposite of "[[open]]". Close the current database connection. ** Require a -f or --force flag if there are unsaved changes in the ** current check-out or if there is non-empty stash. ** ** Options: ** --force|-f necessary to close a check out with uncommitted changes ** ** See also: [[open]] */ void close_cmd(void){ int forceFlag = find_option("force","f",0)!=0; db_must_be_within_tree(); /* We should be done with options.. */ verify_all_options(); |
︙ | ︙ |
Changes to src/clone.c.
︙ | ︙ | |||
81 82 83 84 85 86 87 | /* ** COMMAND: clone ** ** Usage: %fossil clone ?OPTIONS? URI FILENAME ** ** Make a clone of a repository specified by URI in the local | | < < > > > > | | | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | /* ** COMMAND: clone ** ** Usage: %fossil clone ?OPTIONS? URI FILENAME ** ** Make a clone of a repository specified by URI in the local ** file named FILENAME. URI may be one of the following forms: ** ([...] denotes optional elements): ** ** * HTTP/HTTPS protocol: ** ** http[s]://[userid[:password]@]host[:port][/path] ** ** * SSH protocol: ** ** ssh://[userid@]host[:port]/path/to/repo.fossil[?fossil=path/fossil.exe] ** ** * Filesystem: ** ** [file://]path/to/repo.fossil ** ** Note that in Fossil (in contrast to some other DVCSes) a repository ** is distinct from a checkout. This command create a clone of a repository. ** Use the separate [[open]] command to open a checkout from that repository. ** ** For ssh and filesystem, path must have an extra leading ** '/' to use an absolute path. ** ** Use %HH escapes for special characters in the userid and ** password. For example "%40" in place of "@", "%2f" in place ** of "/", and "%3a" in place of ":". ** ** By default, the current login name is used to create the default ** admin user. This can be overridden using the -A|--admin-user ** parameter. ** ** Options: ** --admin-user|-A USERNAME Make USERNAME the administrator ** --httpauth|-B USER:PASS Add HTTP Basic Authorization to requests ** --nocompress Omit extra delta compression ** --once Don't remember the URI. ** --private Also clone private branches ** --save-http-password Remember the HTTP password without asking ** --ssh-command|-c SSH Use SSH as the "ssh" command ** --ssl-identity FILENAME Use the SSL identity if requested by the server ** -u|--unversioned Also sync unversioned content ** -v|--verbose Show more statistics in output ** ** See also: [[init]], [[open]] */ void clone_cmd(void){ char *zPassword; const char *zDefaultUser; /* Optional name of the default user */ const char *zHttpAuth; /* HTTP Authorization user:pass information */ int nErr = 0; int urlFlags = URL_PROMPT_PW | URL_REMEMBER; |
︙ | ︙ |
Changes to src/codecheck1.c.
︙ | ︙ | |||
405 406 407 408 409 410 411 | { "mprintf", 1, FMT_SAFE }, { "pop3_print", 2, FMT_SAFE }, { "smtp_send_line", 2, FMT_SAFE }, { "smtp_server_send", 2, FMT_SAFE }, { "socket_set_errmsg", 1, FMT_SAFE }, { "ssl_set_errmsg", 1, FMT_SAFE }, { "style_header", 1, FMT_HTML }, | < | 405 406 407 408 409 410 411 412 413 414 415 416 417 418 | { "mprintf", 1, FMT_SAFE }, { "pop3_print", 2, FMT_SAFE }, { "smtp_send_line", 2, FMT_SAFE }, { "smtp_server_send", 2, FMT_SAFE }, { "socket_set_errmsg", 1, FMT_SAFE }, { "ssl_set_errmsg", 1, FMT_SAFE }, { "style_header", 1, FMT_HTML }, { "style_set_current_page", 1, FMT_URL }, { "style_submenu_element", 2, FMT_URL }, { "style_submenu_sql", 3, FMT_SQL }, { "webpage_error", 1, FMT_SAFE }, { "xhref", 2, FMT_URL }, }; |
︙ | ︙ |
Changes to src/configure.c.
︙ | ︙ | |||
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | { "footer", CONFIGSET_SKIN }, { "details", CONFIGSET_SKIN }, { "js", CONFIGSET_SKIN }, { "logo-mimetype", CONFIGSET_SKIN }, { "logo-image", CONFIGSET_SKIN }, { "background-mimetype", CONFIGSET_SKIN }, { "background-image", CONFIGSET_SKIN }, { "timeline-block-markup", CONFIGSET_SKIN }, { "timeline-date-format", CONFIGSET_SKIN }, { "timeline-default-style", CONFIGSET_SKIN }, { "timeline-dwelltime", CONFIGSET_SKIN }, { "timeline-closetime", CONFIGSET_SKIN }, { "timeline-max-comment", CONFIGSET_SKIN }, { "timeline-plaintext", CONFIGSET_SKIN }, { "timeline-truncate-at-blank", CONFIGSET_SKIN }, { "timeline-tslink-info", CONFIGSET_SKIN }, { "timeline-utc", CONFIGSET_SKIN }, { "adunit", CONFIGSET_SKIN }, { "adunit-omit-if-admin", CONFIGSET_SKIN }, { "adunit-omit-if-user", CONFIGSET_SKIN }, { "default-csp", CONFIGSET_SKIN }, { "sitemap-docidx", CONFIGSET_SKIN }, { "sitemap-download", CONFIGSET_SKIN }, { "sitemap-license", CONFIGSET_SKIN }, { "sitemap-contact", CONFIGSET_SKIN }, #ifdef FOSSIL_ENABLE_TH1_DOCS { "th1-docs", CONFIGSET_TH1 }, #endif #ifdef FOSSIL_ENABLE_TH1_HOOKS { "th1-hooks", CONFIGSET_TH1 }, #endif | > > > | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | { "footer", CONFIGSET_SKIN }, { "details", CONFIGSET_SKIN }, { "js", CONFIGSET_SKIN }, { "logo-mimetype", CONFIGSET_SKIN }, { "logo-image", CONFIGSET_SKIN }, { "background-mimetype", CONFIGSET_SKIN }, { "background-image", CONFIGSET_SKIN }, { "icon-mimetype", CONFIGSET_SKIN }, { "icon-image", CONFIGSET_SKIN }, { "timeline-block-markup", CONFIGSET_SKIN }, { "timeline-date-format", CONFIGSET_SKIN }, { "timeline-default-style", CONFIGSET_SKIN }, { "timeline-dwelltime", CONFIGSET_SKIN }, { "timeline-closetime", CONFIGSET_SKIN }, { "timeline-max-comment", CONFIGSET_SKIN }, { "timeline-plaintext", CONFIGSET_SKIN }, { "timeline-truncate-at-blank", CONFIGSET_SKIN }, { "timeline-tslink-info", CONFIGSET_SKIN }, { "timeline-utc", CONFIGSET_SKIN }, { "adunit", CONFIGSET_SKIN }, { "adunit-omit-if-admin", CONFIGSET_SKIN }, { "adunit-omit-if-user", CONFIGSET_SKIN }, { "default-csp", CONFIGSET_SKIN }, { "sitemap-docidx", CONFIGSET_SKIN }, { "sitemap-download", CONFIGSET_SKIN }, { "sitemap-license", CONFIGSET_SKIN }, { "sitemap-contact", CONFIGSET_SKIN }, { "safe-html", CONFIGSET_SKIN }, #ifdef FOSSIL_ENABLE_TH1_DOCS { "th1-docs", CONFIGSET_TH1 }, #endif #ifdef FOSSIL_ENABLE_TH1_HOOKS { "th1-hooks", CONFIGSET_TH1 }, #endif |
︙ | ︙ | |||
742 743 744 745 746 747 748 | ** ** Synchronize configuration changes in the local repository with ** the remote repository at URL. ** ** Options: ** -R|--repository FILE Extract info from repository FILE ** | | | 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 | ** ** Synchronize configuration changes in the local repository with ** the remote repository at URL. ** ** Options: ** -R|--repository FILE Extract info from repository FILE ** ** See also: [[settings]], [[unset]] */ void configuration_cmd(void){ int n; const char *zMethod; db_find_and_open_repository(0, 0); db_open_config(0, 0); if( g.argc<3 ){ |
︙ | ︙ |
Changes to src/content.c.
︙ | ︙ | |||
324 325 326 327 328 329 330 | ** Extract an artifact by its artifact hash and write the results on ** standard output, or if the optional 4th argument is given, in ** the named output file. ** ** Options: ** -R|--repository FILE Extract artifacts from repository FILE ** | | | 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 | ** Extract an artifact by its artifact hash and write the results on ** standard output, or if the optional 4th argument is given, in ** the named output file. ** ** Options: ** -R|--repository FILE Extract artifacts from repository FILE ** ** See also: [[finfo]] */ void artifact_cmd(void){ int rid; Blob content; const char *zFile; db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); if( g.argc!=4 && g.argc!=3 ) usage("ARTIFACT-ID ?FILENAME? ?OPTIONS?"); |
︙ | ︙ |
Changes to src/db.c.
︙ | ︙ | |||
114 115 116 117 118 119 120 121 122 123 124 125 126 127 | ** All static variable that a used by only this file are gathered into ** the following structure. */ static struct DbLocalData { int nBegin; /* Nesting depth of BEGIN */ int doRollback; /* True to force a rollback */ int nCommitHook; /* Number of commit hooks */ Stmt *pAllStmt; /* List of all unfinalized statements */ int nPrepare; /* Number of calls to sqlite3_prepare_v2() */ int nDeleteOnFail; /* Number of entries in azDeleteOnFail[] */ struct sCommitHook { int (*xHook)(void); /* Functions to call at db_end_transaction() */ int sequence; /* Call functions in sequence order */ } aHook[5]; | > | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | ** All static variable that a used by only this file are gathered into ** the following structure. */ static struct DbLocalData { int nBegin; /* Nesting depth of BEGIN */ int doRollback; /* True to force a rollback */ int nCommitHook; /* Number of commit hooks */ int wrTxn; /* Outer-most TNX is a write */ Stmt *pAllStmt; /* List of all unfinalized statements */ int nPrepare; /* Number of calls to sqlite3_prepare_v2() */ int nDeleteOnFail; /* Number of entries in azDeleteOnFail[] */ struct sCommitHook { int (*xHook)(void); /* Functions to call at db_end_transaction() */ int sequence; /* Call functions in sequence order */ } aHook[5]; |
︙ | ︙ | |||
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | if( db.nBegin==0 ){ db_multi_exec("BEGIN"); sqlite3_commit_hook(g.db, db_verify_at_commit, 0); db.nPriorChanges = sqlite3_total_changes(g.db); db.doRollback = 0; db.zStartFile = zStartFile; db.iStartLine = iStartLine; } db.nBegin++; } /* ** Begin a new transaction for writing. */ void db_begin_write_real(const char *zStartFile, int iStartLine){ if( db.nBegin==0 ){ db_multi_exec("BEGIN IMMEDIATE"); sqlite3_commit_hook(g.db, db_verify_at_commit, 0); db.nPriorChanges = sqlite3_total_changes(g.db); db.doRollback = 0; db.zStartFile = zStartFile; db.iStartLine = iStartLine; | > > | | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | if( db.nBegin==0 ){ db_multi_exec("BEGIN"); sqlite3_commit_hook(g.db, db_verify_at_commit, 0); db.nPriorChanges = sqlite3_total_changes(g.db); db.doRollback = 0; db.zStartFile = zStartFile; db.iStartLine = iStartLine; db.wrTxn = 0; } db.nBegin++; } /* ** Begin a new transaction for writing. */ void db_begin_write_real(const char *zStartFile, int iStartLine){ if( db.nBegin==0 ){ db_multi_exec("BEGIN IMMEDIATE"); sqlite3_commit_hook(g.db, db_verify_at_commit, 0); db.nPriorChanges = sqlite3_total_changes(g.db); db.doRollback = 0; db.zStartFile = zStartFile; db.iStartLine = iStartLine; db.wrTxn = 1; }else if( !db.wrTxn ){ fossil_warning("read txn at %s:%d might cause SQLITE_BUSY " "for the write txn at %s:%d", db.zStartFile, db.iStartLine, zStartFile, iStartLine); } db.nBegin++; } |
︙ | ︙ | |||
1326 1327 1328 1329 1330 1331 1332 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, g.zVfsName ); if( rc!=SQLITE_OK ){ db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); } db_maybe_set_encryption_key(db, zDbName); | | | | 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, g.zVfsName ); if( rc!=SQLITE_OK ){ db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); } db_maybe_set_encryption_key(db, zDbName); sqlite3_busy_timeout(db, 15000); sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); sqlite3_create_function( db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 ); sqlite3_create_function( db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 ); if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); db_add_aux_functions(db); re_add_sql_func(db); /* The REGEXP operator */ foci_register(db); /* The "files_of_checkin" virtual table */ sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); return db; } /* ** Detaches the zLabel database. */ |
︙ | ︙ | |||
1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 | ** For legacy, also look for ".fos". The use of ".fos" is deprecated ** since "fos" has negative connotations in Hungarian, we are told. ** ** If no valid _FOSSIL_ or .fslckout file is found, we move up one level and ** try again. Once the file is found, the g.zLocalRoot variable is set ** to the root of the repository tree and this routine returns 1. If ** no database is found, then this routine return 0. ** ** This routine always opens the user database regardless of whether or ** not the repository database is found. If the _FOSSIL_ or .fslckout file ** is found, it is attached to the open database connection too. */ | > > > > | | 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 | ** For legacy, also look for ".fos". The use of ".fos" is deprecated ** since "fos" has negative connotations in Hungarian, we are told. ** ** If no valid _FOSSIL_ or .fslckout file is found, we move up one level and ** try again. Once the file is found, the g.zLocalRoot variable is set ** to the root of the repository tree and this routine returns 1. If ** no database is found, then this routine return 0. ** ** In db_open_local_v2(), if the bRootOnly flag is true, then only ** look in the CWD for the checkout database. Do not scan upwards in ** the file hierarchy. ** ** This routine always opens the user database regardless of whether or ** not the repository database is found. If the _FOSSIL_ or .fslckout file ** is found, it is attached to the open database connection too. */ int db_open_local_v2(const char *zDbName, int bRootOnly){ int i, n; char zPwd[2000]; static const char *(aDbName[]) = { "_FOSSIL_", ".fslckout", ".fos" }; if( g.localOpen ) return 1; file_getcwd(zPwd, sizeof(zPwd)-20); n = strlen(zPwd); |
︙ | ︙ | |||
1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 | } g.zLocalRoot = mprintf("%s/", zPwd); g.localOpen = 1; db_open_repository(zDbName); return 1; } } n--; while( n>1 && zPwd[n]!='/' ){ n--; } while( n>1 && zPwd[n-1]=='/' ){ n--; } zPwd[n] = 0; } /* A checkout database file could not be found */ return 0; } /* ** Get the full pathname to the repository database file. The ** local database (the _FOSSIL_ or .fslckout database) must have already ** been opened before this routine is called. */ | > > > > | 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 | } g.zLocalRoot = mprintf("%s/", zPwd); g.localOpen = 1; db_open_repository(zDbName); return 1; } } if( bRootOnly ) break; n--; while( n>1 && zPwd[n]!='/' ){ n--; } while( n>1 && zPwd[n-1]=='/' ){ n--; } zPwd[n] = 0; } /* A checkout database file could not be found */ return 0; } int db_open_local(const char *zDbName){ return db_open_local_v2(zDbName, 0); } /* ** Get the full pathname to the repository database file. The ** local database (the _FOSSIL_ or .fslckout database) must have already ** been opened before this routine is called. */ |
︙ | ︙ | |||
2386 2387 2388 2389 2390 2391 2392 | ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be replaced by ** a space, and it may also name a timezone offset from UTC as "-HH:MM" ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" ** means UTC. ** | | | 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 | ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be replaced by ** a space, and it may also name a timezone offset from UTC as "-HH:MM" ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" ** means UTC. ** ** See also: [[clone]] */ void create_repository_cmd(void){ char *zPassword; const char *zTemplate; /* Repository from which to copy settings */ const char *zDate; /* Date of the initial check-in */ const char *zDefaultUser; /* Optional name of the default user */ int bUseSha1 = 0; /* True to set the hash-policy to sha1 */ |
︙ | ︙ | |||
3068 3069 3070 3071 3072 3073 3074 | } blob_reset(&full); } /* ** COMMAND: open ** | | | | > | > > > > > > > > > | < > > > > > > > > > | > > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 | } blob_reset(&full); } /* ** COMMAND: open ** ** Usage: %fossil open REPOSITORY ?VERSION? ?OPTIONS? ** ** Open a new connection to the repository name REPOSITORY. A checkout ** for the repository is created with its root at the current working ** directory, or in DIR if the "--workdir DIR" is used. If VERSION is ** specified then that version is checked out. Otherwise the most recent ** check-in on the main branch (usually "trunk") is used. ** ** REPOSITORY can be the filename for a repository that already exists on the ** local machine or it can be a URI for a remote repository. If REPOSITORY ** is a URI in one of the formats recognized by the [[clone]] command, then ** remote repo is first cloned, then the clone is opened. The clone will be ** stored in the current directory, or in DIR if the "--repodir DIR" option ** is used. The name of the clone will be taken from the last term of the URI. ** For "http:" and "https:" URIs, you can append an extra term to the end of ** the URI to get any repository name you like. For example: ** ** fossil open https://fossil-scm.org/home/new-name ** ** The base URI for cloning is "https://fossil-scm.org/home". The extra ** "new-name" term means that the cloned repository will be called ** "new-name.fossil". ** ** Options: ** --empty Initialize checkout as being empty, but still connected ** with the local repository. If you commit this checkout, ** it will become a new "initial" commit in the repository. ** --force Continue with the open even if the working directory is ** not empty. ** --force-missing Force opening a repository with missing content ** --keep Only modify the manifest and manifest.uuid files ** --nested Allow opening a repository inside an opened checkout ** --repodir DIR If REPOSITORY is a URI that will be cloned, store ** the clone in DIR rather than in "." ** --setmtime Set timestamps of all files to match their SCM-side ** times (the timestamp of the last checkin which modified ** them). ** --workdir DIR Use DIR as the working directory instead of ".". The DIR ** directory is created if it does not exist. ** ** See also: [[close]], [[clone]] */ void cmd_open(void){ int emptyFlag; int keepFlag; int forceMissingFlag; int allowNested; int allowSymlinks; int setmtimeFlag; /* --setmtime. Set mtimes on files */ int bForce = 0; /* --force. Open even if non-empty dir */ static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 }; const char *zWorkDir; /* --workdir value */ const char *zRepo = 0; /* Name of the repository file */ const char *zRepoDir = 0; /* --repodir value */ char *zPwd; /* Initial working directory */ int isUri = 0; /* True if REPOSITORY is a URI */ url_proxy_options(); emptyFlag = find_option("empty",0,0)!=0; keepFlag = find_option("keep",0,0)!=0; forceMissingFlag = find_option("force-missing",0,0)!=0; allowNested = find_option("nested",0,0)!=0; setmtimeFlag = find_option("setmtime",0,0)!=0; zWorkDir = find_option("workdir",0,1); zRepoDir = find_option("repodir",0,1); bForce = find_option("force",0,0)!=0; zPwd = file_getcwd(0,0); /* We should be done with options.. */ verify_all_options(); if( g.argc!=3 && g.argc!=4 ){ usage("REPOSITORY-FILENAME ?VERSION?"); } zRepo = g.argv[2]; if( sqlite3_strglob("http://*", zRepo)==0 || sqlite3_strglob("https://*", zRepo)==0 || sqlite3_strglob("ssh:*", zRepo)==0 || sqlite3_strglob("file:*", zRepo)==0 ){ isUri = 1; } /* If --workdir is specified, change to the requested working directory */ if( zWorkDir ){ if( !isUri ){ zRepo = file_canonical_name_dup(zRepo); } if( zRepoDir ){ zRepoDir = file_canonical_name_dup(zRepoDir); } if( file_isdir(zWorkDir, ExtFILE)!=1 ){ file_mkfolder(zWorkDir, ExtFILE, 0, 0); if( file_mkdir(zWorkDir, ExtFILE, 0) ){ fossil_fatal("cannot create directory %s", zWorkDir); } } if( file_chdir(zWorkDir, 0) ){ fossil_fatal("unable to make %s the working directory", zWorkDir); } } if( keepFlag==0 && bForce==0 && file_directory_size(".", 0, 1)>0 ){ fossil_fatal("directory %s is not empty\n" "use the --force option to override", file_getcwd(0,0)); } if( db_open_local_v2(0, allowNested) ){ fossil_fatal("there is already an open tree at %s", g.zLocalRoot); } /* If REPOSITORY looks like a URI, then try to clone it first */ if( isUri ){ char *zNewBase; /* Base name of the cloned repository file */ const char *zUri; /* URI to clone */ int i; /* Loop counter */ int rc; /* Result code from fossil_system() */ Blob cmd; /* Clone command to be run */ char *zCmd; /* String version of the clone command */ zUri = zRepo; zNewBase = fossil_strdup(file_tail(zUri)); for(i=(int)strlen(zNewBase)-1; i>1 && zNewBase[i]!='.'; i--){} if( zNewBase[i]=='.' ) zNewBase[i] = 0; if( zRepoDir==0 ) zRepoDir = zPwd; zRepo = mprintf("%s/%s.fossil", zRepoDir, zNewBase); fossil_free(zNewBase); blob_init(&cmd, 0, 0); blob_append_escaped_arg(&cmd, g.nameOfExe); blob_append(&cmd, " clone", -1); blob_append_escaped_arg(&cmd, zUri); blob_append_escaped_arg(&cmd, zRepo); zCmd = blob_str(&cmd); fossil_print("%s\n", zCmd); if( zWorkDir ) file_chdir(zPwd, 0); rc = fossil_system(zCmd); if( rc ){ fossil_fatal("clone of %s failed", zUri); } blob_reset(&cmd); if( zWorkDir ) file_chdir(zWorkDir, 0); }else if( zRepoDir ){ fossil_fatal("the --repodir option only makes sense if the REPOSITORY " "argument is a URI that begins with http:, https:, ssh:, " "or file:"); } db_open_repository(zRepo); /* Figure out which revision to open. */ if( !emptyFlag ){ if( g.argc==4 ){ g.zOpenRevision = g.argv[3]; }else if( db_exists("SELECT 1 FROM event WHERE type='ci'") ){ g.zOpenRevision = db_get("main-branch", 0); |
︙ | ︙ | |||
3167 3168 3169 3170 3171 3172 3173 | }else{ /* Since the local checkout may not have any files at this ** point, this will probably be the setting value from the ** repository or global configuration databases. */ g.allowSymlinks = db_get_boolean("allow-symlinks", db_allow_symlinks_by_default()); } | | | | 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 | }else{ /* Since the local checkout may not have any files at this ** point, this will probably be the setting value from the ** repository or global configuration databases. */ g.allowSymlinks = db_get_boolean("allow-symlinks", db_allow_symlinks_by_default()); } db_lset("repository", zRepo); db_record_repository_filename(zRepo); db_set_checkout(0); azNewArgv[0] = g.argv[0]; g.argv = azNewArgv; if( !emptyFlag ){ g.argc = 3; if( g.zOpenRevision ){ azNewArgv[g.argc-1] = g.zOpenRevision; |
︙ | ︙ | |||
3885 3886 3887 3888 3889 3890 3891 | ** ** Options: ** --global set or unset the given property globally instead of ** setting or unsetting it for the open repository only. ** ** --exact only consider exact name matches. ** | | | 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 | ** ** Options: ** --global set or unset the given property globally instead of ** setting or unsetting it for the open repository only. ** ** --exact only consider exact name matches. ** ** See also: [[configuration]] */ void setting_cmd(void){ int i; int globalFlag = find_option("global","g",0)!=0; int exactFlag = find_option("exact",0,0)!=0; int unsetFlag = g.argv[1][0]=='u'; int nSetting; |
︙ | ︙ |
Changes to src/default.css.
︙ | ︙ | |||
438 439 440 441 442 443 444 | } span.usertype:before { content:"'"; } span.usertype:after { content:"'"; } | < < < < < < | 438 439 440 441 442 443 444 445 446 447 448 449 450 451 | } span.usertype:before { content:"'"; } span.usertype:after { content:"'"; } p.missingPriv { color: blue; } span.wikiruleHead { font-weight: bold; } td.tktDspLabel { |
︙ | ︙ | |||
772 773 774 775 776 777 778 779 | } div.forumHier > div > form, div.forumTime > div > form, div.forumHierRoot > div > form { margin: 0.5em 0; } .forum-post-collapser { font-size: 0.8em; | > > < > > > > > > > > < < < < < | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 | } div.forumHier > div > form, div.forumTime > div > form, div.forumHierRoot > div > form { margin: 0.5em 0; } .forum-post-collapser { /* Common style for the bottom-of-post and right-of-post expand/collapse widgets. */ font-size: 0.8em; padding: 0; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 0 0 0.5em 0.5em; background-color: rgba(0, 0, 0, 0.05); opacity: 0.8; cursor: pointer; } .forum-post-collapser.bottom { margin: 0 0 0.4em 0; height: 1.75em; line-height: 1.75em; /* ^^^ Those sizes are finely tuned for the current selection of arrow characters. If those change, these should, too. Remember that FF/Chrome simply do not agree on alignment with most values :/. */ display: flex; flex-direction: row; justify-content: space-between; } .forum-post-collapser.bottom > span { margin: 0 1em 0 1em; vertical-align: middle; } .forum-post-collapser.bottom > span::before { content: "⇣⇣⇣"; } .forum-post-collapser.bottom.expanded > span::before { content: "⇡⇡⇡" /*reminder: FF/Chrome cannot agree on alignment of ⮝*/; } div.forumPostBody{ max-height: 50em; overflow: auto; } div.forumPostBody.with-expander { display: flex; flex-direction: row; overflow: auto; } div.forumPostBody.with-expander:not(.expanded) > :first-child { overflow-y: hidden; } div.forumPostBody.with-expander > *:first-child { /* Main content DIV/PRE */ overflow: auto; flex: 10 1 auto; } div.forumPostBody.with-expander.expanded > *:first-child { margin-bottom: 0.5em /* try to suppress scroll bar */; } div.forumPostBody.with-expander .forum-post-collapser.right { /* "Tap zone" for expansion of the post, sits to the right of the post's content. */ flex: 1 10 auto; min-width: 1.25em; max-width: 1.25em; margin: 0 0 0 0.2em; overflow: hidden; display: flex; flex-direction: column; justify-content: space-around; align-items: center; border-radius: 0.1em; cursor: pointer; border-bottom: 0; border-radius: 0 0.5em 0 0; } div.forumPostBody.with-expander .forum-post-collapser.right > span:before { content: "⇣"; } div.forumPostBody.with-expander.expanded .forum-post-collapser.right > span:before { content: "⇡"; } div.forumPostBody.expanded { max-height: initial; } div.forumPostBody.shrunken { /* When an expandable post is un-expanded, it is shrunkend down to this size instead of its original size. */ max-height: 8em; } div.forumSel { background-color: #cef; } div.forumObs { color: #bbb; } |
︙ | ︙ | |||
902 903 904 905 906 907 908 | } .warning { color: darkred; background: yellow; opacity: 0.7; } .hidden { | > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 | } .warning { color: darkred; background: yellow; opacity: 0.7; } .hidden { /* The framework-wide way of hiding elements is to assign them this CSS class. To make them visible again, remove it. The !important qualifiers are unfortunate but sometimes necessary when hidden element has other classes which specify visibility-related options. */ position: absolute !important; opacity: 0 !important; pointer-events: none !important; display: none !important; } input { max-width: 95%; } textarea { max-width: 95%; } img { max-width: 100%; height: auto; } hr { /* Needed to keep /dir README.txt from floating right in some skins */ clear: both; } /** .tab-xxx: styles for fossil.tabs.js. */ .tab-container { width: 100%; display: flex; flex-direction: column; align-items: stretch; } .tab-container > #fossil-status-bar { margin-top: 0; } .tab-container > .tabs { padding: 0.25em; margin: 0; display: flex; flex-direction: column; border-width: 1px; border-style: outset; border-color: inherit; } .tab-container > .tabs > .tab-panel { align-self: stretch; flex: 10 1 auto; display: block; border: 0; padding: 0; margin: 0; } .tab-container > .tab-bar { display: flex; flex-direction: row; flex: 1 10 auto; align-self: stretch; flex-wrap: wrap; } .tab-container > .tab-bar > .tab-button { display: inline-block; border-radius: 0.25em 0.25em 0 0; margin: 0 0.1em; padding: 0.25em 0.75em; align-self: baseline; border-color: inherit; border-width: 1px; border-bottom: none; border-top-style: inset; border-left-style: inset; border-right-style: inset; cursor: pointer; opacity: 0.6; } .tab-container > .tab-bar > .tab-button.selected { text-decoration: underline; opacity: 1.0; border-top-style: outset; border-left-style: outset; border-right-style: outset; } /** The flex-xxx classes can be used to create basic flexbox layouts through the application of classes to the containing/contained objects. */ .flex-container { display: flex; } .flex-container.flex-row { flex-direction: row; flex-wrap: wrap; justify-content: center; align-items: center; } .flex-container .flex-grow { flex-grow: 10; flex-shrink: 0; } .flex-container .flex-shrink { flex-grow: 0; flex-shrink: 10; } .flex-container.flex-row.stretch { flex-wrap: wrap; align-items: baseline; justify-content: stretch; margin: 0; } .flex-container.flex-column { flex-direction: column; flex-wrap: wrap; justify-content: center; align-items: center; } .flex-container.flex-column.stretch { align-items: stretch; margin: 0; } .flex-container.child-gap-small > * { margin: 0.25em; } #fossil-status-bar { display: block; font-family: monospace; border-width: 1px; border-style: inset; border-color: inherit; min-height: 1.5em; font-size: 1.2em; padding: 0.2em; margin: 0.25em 0; flex: 0 0 auto; } .font-size-100 { font-size: 100%; } .font-size-125 { font-size: 125%; } .font-size-150 { font-size: 150%; } .font-size-175 { font-size: 175%; } .font-size-200 { font-size: 200%; } /** .input-with-label is intended to be a wrapper element which contain both a LABEL tag and an INPUT or SELECT control. The wrapper is "necessary", as opposed to placing the INPUT in the LABEL, so that we can include multiple INPUT elements (e.g. a set of radio buttons). */ .input-with-label { border: 1px inset #808080; border-radius: 0.25em; padding: 0.25em 0.4em; margin: 0 0.5em; display: inline-block; cursor: default; } .input-with-label > * { vertical-align: middle; } .input-with-label > label { display: inline; /* some skins set label display to block! */ } .input-with-label > input { margin: 0; } .input-with-label > button { margin: 0; } .input-with-label > select { margin: 0; } .input-with-label > input[type=text] { margin: 0; } .input-with-label > textarea { margin: 0; } .input-with-label > input[type=checkbox] { vertical-align: sub; } .input-with-label > input[type=radio] { vertical-align: sub; } .input-with-label > label { font-weight: initial; margin: 0 0.25em 0 0.25em; vertical-align: middle; } table.numbered-lines { width: 100%; table-layout: fixed /* required to keep ultra-wide code from exceeding window width, and instead force a scrollbar on them. */; } table.numbered-lines > tbody > tr { font-family: monospace; font-size: 1.2em; line-height: 1.35; white-space: pre; } table.numbered-lines > tbody > tr > td { font-family: inherit; font-size: inherit; line-height: inherit; white-space: inherit; margin: 0; vertical-align: top; padding: 0.25em 0 0 0 /*prevents slight overlap at top */; } table.numbered-lines td.line-numbers { width: 4.5em; } table.numbered-lines td.line-numbers > span:first-of-type { margin-top: 0.25em/*must match top PADDING of td.file-content > pre > code*/; } table.numbered-lines td.line-numbers > span { display: block; margin: 0; padding: 0; line-height: inherit; font-size: inherit; font-family: inherit; cursor: pointer; white-space: pre; margin-right: 2px/*keep selection from nudging the right column */; text-align: right; } table.numbered-lines td.line-numbers > span:hover { background-color: rgba(112, 112, 112, 0.25); } table.numbered-lines td.file-content { padding-left: 0.25em; } table.numbered-lines td.file-content > pre, table.numbered-lines td.file-content > pre > code { margin: 0; padding: 0; line-height: inherit; font-size: inherit; font-family: inherit; white-space: pre; display: block/*necessary for certain skins!*/; } table.numbered-lines td.file-content > pre { } table.numbered-lines td.file-content > pre > code { overflow: auto; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.25em/*any top padding here must match the top MARGIN of td.line-numbers's first span child or the lines/code will get misaligned. */; padding-bottom: 0.25em/*prevents a slight overlap at bottom from triggering a scroller*/; } table.numbered-lines td.file-content > pre > code > * { /* Defense against syntax highlighters indirectly messing up these properties... */ line-height: inherit; font-size: inherit; font-family: inherit; } table.numbered-lines td.line-numbers span.selected-line/*replacement*/ { font-weight: bold; color: blue; background-color: #d5d5ff; border: 1px blue solid; border-top-width: 0; border-bottom-width: 0; padding: 0; margin: 0; } table.numbered-lines td.line-numbers span.selected-line.start { border-top-width: 1px; margin-top: -1px/*restore alignment*/; } table.numbered-lines td.line-numbers span.selected-line.end { border-bottom-width: 1px; margin-top: -1px/*restore alignment*/; } table.numbered-lines td.line-numbers span.selected-line.start.end { margin-top: -2px/*restore alignment*/; } .fossil-tooltip { text-align: center; padding: 0.2em 1em; border: 1px solid black; border-radius: 0.25em; position: absolute; display: inline-block; z-index: 100; box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.75); background-color: inherit; font-size: 80%; } .fossil-toast {/* "toast"-style popup message */ padding: 0.25em 0.5em; margin: 0; border-radius: 0.25em; font-size: 1em; opacity: 0.8; border-size: 1px; border-style: dotted; border-color: rgb( 127, 127, 127, 0.5 ); } blockquote.file-content { /* file content block in the /file page */ margin: 0 1em; } |
Changes to src/descendants.c.
︙ | ︙ | |||
346 347 348 349 350 351 352 | ** ** Options: ** -R|--repository FILE Extract info from repository FILE ** -W|--width <num> Width of lines (default is to auto-detect). ** Must be >20 or 0 (= no limit, resulting in a ** single line per entry). ** | | | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | ** ** Options: ** -R|--repository FILE Extract info from repository FILE ** -W|--width <num> Width of lines (default is to auto-detect). ** Must be >20 or 0 (= no limit, resulting in a ** single line per entry). ** ** See also: [[finfo]], [[info]], [[leaves]] */ void descendants_cmd(void){ Stmt q; int base, width; const char *zWidth; db_find_and_open_repository(0,0); |
︙ | ︙ | |||
406 407 408 409 410 411 412 | ** -c|--closed show only closed leaves ** -m|--multiple show only cases with multiple leaves on a single branch ** --recompute recompute the "leaf" table in the repository DB ** -W|--width <num> Width of lines (default is to auto-detect). Must be ** >39 or 0 (= no limit, resulting in a single line per ** entry). ** | | | 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | ** -c|--closed show only closed leaves ** -m|--multiple show only cases with multiple leaves on a single branch ** --recompute recompute the "leaf" table in the repository DB ** -W|--width <num> Width of lines (default is to auto-detect). Must be ** >39 or 0 (= no limit, resulting in a single line per ** entry). ** ** See also: [[descendants]], [[finfo]], [[info]], [[branch]] */ void leaves_cmd(void){ Stmt q; Blob sql; int showAll = find_option("all", "a", 0)!=0; int showClosed = find_option("closed", "c", 0)!=0; int recomputeFlag = find_option("recompute",0,0)!=0; |
︙ | ︙ |
Changes to src/diff.c.
︙ | ︙ | |||
123 124 125 126 127 128 129 | /* ** Count the number of lines in the input string. Include the last line ** in the count even if it lacks the \n terminator. If an empty string ** is specified, the number of lines is zero. For the purposes of this ** function, a string is considered empty if it contains no characters ** -OR- it contains only NUL characters. */ | | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | /* ** Count the number of lines in the input string. Include the last line ** in the count even if it lacks the \n terminator. If an empty string ** is specified, the number of lines is zero. For the purposes of this ** function, a string is considered empty if it contains no characters ** -OR- it contains only NUL characters. */ int count_lines( const char *z, int n, int *pnLine ){ int nLine; const char *zNL, *z2; for(nLine=0, z2=z; (zNL = strchr(z2,'\n'))!=0; z2=zNL+1, nLine++){} |
︙ | ︙ | |||
2560 2561 2562 2563 2564 2565 2566 | @ </pre> style_footer(); } /* ** COMMAND: annotate ** COMMAND: blame | | | 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 | @ </pre> style_footer(); } /* ** COMMAND: annotate ** COMMAND: blame ** COMMAND: praise* ** ** Usage: %fossil annotate|blame|praise ?OPTIONS? FILENAME ** ** Output the text of a file with markings to show when each line of the file ** was last modified. The version currently checked out is shown by default. ** Other versions may be specified using the -r option. The "annotate" command ** shows line numbers and omits the username. The "blame" and "praise" commands |
︙ | ︙ | |||
2594 2595 2596 2597 2598 2599 2600 | ** none No limit ** -o|--origin VERSION The origin check-in. By default this is the ** root of the repository. Set to "trunk" or ** similar for a reverse annotation. ** -w|--ignore-all-space Ignore white space when comparing lines ** -Z|--ignore-trailing-space Ignore whitespace at line end ** | | | 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 | ** none No limit ** -o|--origin VERSION The origin check-in. By default this is the ** root of the repository. Set to "trunk" or ** similar for a reverse annotation. ** -w|--ignore-all-space Ignore white space when comparing lines ** -Z|--ignore-trailing-space Ignore whitespace at line end ** ** See also: [[info]], [[finfo]], [[timeline]] */ void annotate_cmd(void){ const char *zRevision; /* Revision name, or NULL for current check-in */ Annotator ann; /* The annotation of the file */ int i; /* Loop counter */ const char *zLimit; /* The value to the -n|--limit option */ const char *zOrig; /* The value for -o|--origin */ |
︙ | ︙ |
Changes to src/dispatch.c.
︙ | ︙ | |||
261 262 263 264 265 266 267 268 269 270 271 272 273 274 | while( j<n && z[j]!=' ' && z[j]!='=' ){ j++; } blob_appendf(pOut, "%#h", j-i, z+i); if( zEnd[0] ) blob_append(pOut, zEnd, -1); i = j; } } } /* ** Attempt to reformat plain-text help into HTML for display on a webpage. ** ** The HTML output is appended to Blob pHtml, which should already be ** initialized. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 | while( j<n && z[j]!=' ' && z[j]!='=' ){ j++; } blob_appendf(pOut, "%#h", j-i, z+i); if( zEnd[0] ) blob_append(pOut, zEnd, -1); i = j; } } } /* ** Input string zIn starts with '['. If the content is a hyperlink of the ** form [[...]] then return the index of the closing ']'. Otherwise return 0. */ static int help_is_link(const char *z, int n){ int i; char c; if( n<5 ) return 0; if( z[1]!='[' ) return 0; for(i=3; i<n && (c = z[i])!=0; i++){ if( c==']' && z[i-1]==']' ) return i; } return 0; } /* ** Append text to pOut, adding hyperlink markup for [...]. */ static void appendLinked(Blob *pOut, const char *z, int n){ int i = 0; int j; while( i<n ){ if( z[i]=='[' && (j = help_is_link(z+i, n-i))>0 ){ if( i ) blob_append(pOut, z, i); z += i+2; n -= i+2; blob_appendf(pOut, "<a href='%R/help?cmd=%.*s'>%.*s</a>", j-3, z, j-3, z); z += j-1; n -= j-1; i = 0; }else{ i++; } } blob_append(pOut, z, i); } /* ** Attempt to reformat plain-text help into HTML for display on a webpage. ** ** The HTML output is appended to Blob pHtml, which should already be ** initialized. ** |
︙ | ︙ | |||
303 304 305 306 307 308 309 310 311 312 313 314 | aIndent[0] = 0; azEnd[0] = ""; while( zHelp[0] ){ i = 0; while( (c = zHelp[i])!=0 && c!='\n' && (c!='%' || strncmp(zHelp+i,"%fossil",7)!=0) ){ i++; } if( c=='%' ){ if( i ) blob_appendf(pHtml, "%#h", i, zHelp); zHelp += i + 1; | > < > > > > > | 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | aIndent[0] = 0; azEnd[0] = ""; while( zHelp[0] ){ i = 0; while( (c = zHelp[i])!=0 && c!='\n' && c!='<' && (c!='%' || strncmp(zHelp+i,"%fossil",7)!=0) ){ i++; } if( c=='%' ){ if( i ) blob_appendf(pHtml, "%#h", i, zHelp); zHelp += i + 1; wantBR = 1; continue; }else if( c=='<' ){ if( i ) blob_appendf(pHtml, "%#h", i, zHelp); blob_append(pHtml, "&", 5); zHelp += i + 1; continue; } if( i>2 && zHelp[0]=='>' && zHelp[1]==' ' ){ isDT = 1; for(nIndent=1; nIndent<i && zHelp[nIndent]==' '; nIndent++){} }else{ isDT = 0; |
︙ | ︙ | |||
392 393 394 395 396 397 398 | blob_append(pHtml, "</dt>\n", 6); } }else if( wantBR ){ appendMixedFont(pHtml, zHelp+nIndent, i-nIndent); blob_append(pHtml, "<br>\n", 5); wantBR = 0; }else{ | | > | > > > > > > > > | 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 | blob_append(pHtml, "</dt>\n", 6); } }else if( wantBR ){ appendMixedFont(pHtml, zHelp+nIndent, i-nIndent); blob_append(pHtml, "<br>\n", 5); wantBR = 0; }else{ appendLinked(pHtml, zHelp+nIndent, i-nIndent); blob_append_char(pHtml, '\n'); } zHelp += i+1; i = 0; if( c==0 ) break; } while( iLevel>0 ){ blob_appendf(pHtml, "%s\n", azEnd[iLevel--]); } } /* ** Format help text for TTY display. */ static void help_to_text(const char *zHelp, Blob *pText){ int i, x; char c; for(i=0; (c = zHelp[i])!=0; i++){ if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){ if( i>0 ) blob_append(pText, zHelp, i); blob_append(pText, "fossil", 6); zHelp += i+7; i = -1; continue; } if( c=='\n' && strncmp(zHelp+i+1,"> ",2)==0 ){ blob_append(pText, zHelp, i+1); blob_append(pText, " ", 1); zHelp += i+2; i = -1; continue; } if( c=='[' && (x = help_is_link(zHelp+i, 100000))!=0 ){ if( i>0 ) blob_append(pText, zHelp, i); zHelp += i+2; blob_append(pText, zHelp, x-3); zHelp += x-1; i = -1; continue; } } if( i>0 ){ blob_append(pText, zHelp, i); } } /* |
︙ | ︙ | |||
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 | ** ** Options: ** -e|--everything Show all commands and pages. ** -t|--test Include test- commands ** -w|--www Show WWW pages. ** -s|--settings Show settings. ** -h|--html Transform output to HTML. */ void test_all_help_cmd(void){ int i; int mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER; int useHtml = find_option("html","h",0)!=0; if( find_option("www","w",0) ){ mask = CMDFLAG_WEBPAGE; } if( find_option("everything","e",0) ){ mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE | CMDFLAG_SETTING | CMDFLAG_TEST; | > > | 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 | ** ** Options: ** -e|--everything Show all commands and pages. ** -t|--test Include test- commands ** -w|--www Show WWW pages. ** -s|--settings Show settings. ** -h|--html Transform output to HTML. ** -r|--raw No output formatting. */ void test_all_help_cmd(void){ int i; int mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER; int useHtml = find_option("html","h",0)!=0; int rawOut = find_option("raw","r",0)!=0; if( find_option("www","w",0) ){ mask = CMDFLAG_WEBPAGE; } if( find_option("everything","e",0) ){ mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE | CMDFLAG_SETTING | CMDFLAG_TEST; |
︙ | ︙ | |||
486 487 488 489 490 491 492 493 494 495 496 497 498 499 | if( useHtml ){ Blob html; blob_init(&html, 0, 0); help_to_html(aCommand[i].zHelp, &html); fossil_print("<h1>%h</h1>\n", aCommand[i].zName); fossil_print("%s\n<hr>\n", blob_str(&html)); blob_reset(&html); }else{ Blob txt; blob_init(&txt, 0, 0); help_to_text(aCommand[i].zHelp, &txt); fossil_print("# %s\n", aCommand[i].zName); fossil_print("%s\n\n", blob_str(&txt)); blob_reset(&txt); | > > > | 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 | if( useHtml ){ Blob html; blob_init(&html, 0, 0); help_to_html(aCommand[i].zHelp, &html); fossil_print("<h1>%h</h1>\n", aCommand[i].zName); fossil_print("%s\n<hr>\n", blob_str(&html)); blob_reset(&html); }else if( rawOut ){ fossil_print("# %s\n", aCommand[i].zName); fossil_print("%s\n\n", aCommand[i].zHelp); }else{ Blob txt; blob_init(&txt, 0, 0); help_to_text(aCommand[i].zHelp, &txt); fossil_print("# %s\n", aCommand[i].zName); fossil_print("%s\n\n", blob_str(&txt)); blob_reset(&txt); |
︙ | ︙ | |||
663 664 665 666 667 668 669 | if( zCmd && *zCmd ){ int rc; const CmdOrPage *pCmd = 0; style_header("Help: %s", zCmd); style_submenu_element("Command-List", "%s/help", g.zTop); | | | 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 | if( zCmd && *zCmd ){ int rc; const CmdOrPage *pCmd = 0; style_header("Help: %s", zCmd); style_submenu_element("Command-List", "%s/help", g.zTop); rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd); if( *zCmd=='/' ){ /* Some of the webpages require query parameters in order to work. ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */ @ <h1>The "%h(zCmd)" page:</h1> }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){ @ <h1>The "%h(pCmd->zName)" setting:</h1> }else{ |
︙ | ︙ | |||
911 912 913 914 915 916 917 | ** ** Display information on how to use TOPIC, which may be a command, webpage, or ** setting. Webpage names begin with "/". If TOPIC is omitted, a list of ** topics is returned. ** ** The following options can be used when TOPIC is omitted: ** | | | 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 | ** ** Display information on how to use TOPIC, which may be a command, webpage, or ** setting. Webpage names begin with "/". If TOPIC is omitted, a list of ** topics is returned. ** ** The following options can be used when TOPIC is omitted: ** ** -a|--all List both common and auxiliary commands ** -o|--options List command-line options common to all commands ** -s|--setting List setting names ** -t|--test List unsupported "test" commands ** -x|--aux List only auxiliary commands ** -w|--www List all web pages ** ** These options can be used when TOPIC is present: |
︙ | ︙ | |||
934 935 936 937 938 939 940 | const CmdOrPage *pCmd = 0; int useHtml = 0; Blob txt; if( g.argc<3 ){ z = g.argv[0]; fossil_print( "Usage: %s help TOPIC\n" | | > | | 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 | const CmdOrPage *pCmd = 0; int useHtml = 0; Blob txt; if( g.argc<3 ){ z = g.argv[0]; fossil_print( "Usage: %s help TOPIC\n" "Try \"%s help help\" or \"%s help -a\" for more options\n" "Frequently used commands:\n", z, z, z); command_list(0, CMDFLAG_1ST_TIER); version_cmd(); return; } if( find_option("options","o",0) ){ fossil_print("%s", zOptions); return; |
︙ | ︙ | |||
1074 1075 1076 1077 1078 1079 1080 | sqlite3_vtab **ppVtab, char **pzErr ){ helptextVtab_vtab *pNew; int rc; rc = sqlite3_declare_vtab(db, | | | 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 | sqlite3_vtab **ppVtab, char **pzErr ){ helptextVtab_vtab *pNew; int rc; rc = sqlite3_declare_vtab(db, "CREATE TABLE x(name,type,flags,helptext,formatted,html)" ); if( rc==SQLITE_OK ){ pNew = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); } |
︙ | ︙ | |||
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 | } case 2: /* flags */ sqlite3_result_int(ctx, pPage->eCmdFlags); break; case 3: /* helptext */ sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC); break; } return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** rowid is the same as the output value. | > > > > > > > > > > > > > > | 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 | } case 2: /* flags */ sqlite3_result_int(ctx, pPage->eCmdFlags); break; case 3: /* helptext */ sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC); break; case 4: { /* formatted */ Blob txt; blob_init(&txt, 0, 0); help_to_text(pPage->zHelp, &txt); sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free); break; } case 5: { /* formatted */ Blob txt; blob_init(&txt, 0, 0); help_to_html(pPage->zHelp, &txt); sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free); break; } } return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** rowid is the same as the output value. |
︙ | ︙ |
Changes to src/doc.c.
︙ | ︙ | |||
145 146 147 148 149 150 151 | { "ips", 3, "application/x-ipscript" }, { "ipx", 3, "application/x-ipix" }, { "jad", 3, "text/vnd.sun.j2me.app-descriptor" }, { "jar", 3, "application/java-archive" }, { "jpe", 3, "image/jpeg" }, { "jpeg", 4, "image/jpeg" }, { "jpg", 3, "image/jpeg" }, | | | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | { "ips", 3, "application/x-ipscript" }, { "ipx", 3, "application/x-ipix" }, { "jad", 3, "text/vnd.sun.j2me.app-descriptor" }, { "jar", 3, "application/java-archive" }, { "jpe", 3, "image/jpeg" }, { "jpeg", 4, "image/jpeg" }, { "jpg", 3, "image/jpeg" }, { "js", 2, "application/javascript" }, { "kar", 3, "audio/midi" }, { "latex", 5, "application/x-latex" }, { "lha", 3, "application/octet-stream" }, { "lsp", 3, "application/x-lisp" }, { "lzh", 3, "application/octet-stream" }, { "m", 1, "text/plain" }, { "m3u", 3, "audio/x-mpegurl" }, |
︙ | ︙ | |||
1130 1131 1132 1133 1134 1135 1136 | cgi_set_content(&bgimg); } /* ** WEBPAGE: favicon.ico ** | | | | | | | > > | > > | > | | | 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 | cgi_set_content(&bgimg); } /* ** WEBPAGE: favicon.ico ** ** Return the configured "favicon.ico" image. If no "favicon.ico" image ** is defined, the returned image is for the Fossil lizard icon. ** ** The intended use case here is to supply an icon for the "fossil ui" ** command. For a permanent website, the recommended process is for ** the admin to set up a project-specific icon and reference that icon ** in the HTML header using a line like: ** ** <link rel="icon" href="URL-FOR-YOUR-ICON" type="MIMETYPE"/> ** */ void favicon_page(void){ Blob icon; char *zMime; etag_check(ETAG_CONFIG, 0); zMime = db_get("icon-mimetype", "image/gif"); blob_zero(&icon); db_blob(&icon, "SELECT value FROM config WHERE name='icon-image'"); if( blob_size(&icon)==0 ){ blob_init(&icon, (char*)aLogo, sizeof(aLogo)); } cgi_set_content_type(zMime); cgi_set_content(&icon); } /* ** WEBPAGE: docsrch ** ** Search for documents that match a user-supplied full-text search pattern. ** If no pattern is specified (by the s= query parameter) then the user |
︙ | ︙ |
Changes to src/etag.c.
︙ | ︙ | |||
95 96 97 98 99 100 101 | */ void etag_check(unsigned eFlags, const char *zHash){ const char *zIfNoneMatch; char zBuf[50]; assert( zETag[0]==0 ); /* Only call this routine once! */ if( etagCancelled ) return; | > > > | | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | */ void etag_check(unsigned eFlags, const char *zHash){ const char *zIfNoneMatch; char zBuf[50]; assert( zETag[0]==0 ); /* Only call this routine once! */ if( etagCancelled ) return; /* By default, ETagged URLs never expire since the ETag will change * when the content changes. Approximate this policy as 10 years. */ iMaxAge = 10 * 365 * 24 * 60 * 60; md5sum_init(); /* Always include the executable ID as part of the hash */ md5sum_step_text("exe-id: ", -1); md5sum_step_text(fossil_exe_id(), -1); md5sum_step_text("\n", 1); |
︙ | ︙ |
Changes to src/export.c.
︙ | ︙ | |||
1640 1641 1642 1643 1644 1645 1646 | } n = db_int(0, "SELECT count(*) FROM mmark WHERE isfile"); k = db_int(0, "SELECT count(*) FROm mmark WHERE NOT isfile"); fossil_print("Exported: %d check-ins and %d file blobs\n", k, n); } /* | | | 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 | } n = db_int(0, "SELECT count(*) FROM mmark WHERE isfile"); k = db_int(0, "SELECT count(*) FROm mmark WHERE NOT isfile"); fossil_print("Exported: %d check-ins and %d file blobs\n", k, n); } /* ** COMMAND: git* ** ** Usage: %fossil git SUBCOMMAND ** ** Do incremental import or export operations between Fossil and Git. ** Subcommands: ** ** > fossil git export [MIRROR] [OPTIONS] |
︙ | ︙ |
Changes to src/file.c.
︙ | ︙ | |||
1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 | ** Compute a canonical pathname for a file or directory. ** Make the name absolute if it is relative. ** Remove redundant / characters ** Remove all /./ path elements. ** Convert /A/../ to just / ** If the slash parameter is non-zero, the trailing slash, if any, ** is retained. */ void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ blob_zero(pOut); if( file_is_absolute_path(zOrigName) ){ blob_appendf(pOut, "%/", zOrigName); }else{ char zPwd[2000]; | > > | 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 | ** Compute a canonical pathname for a file or directory. ** Make the name absolute if it is relative. ** Remove redundant / characters ** Remove all /./ path elements. ** Convert /A/../ to just / ** If the slash parameter is non-zero, the trailing slash, if any, ** is retained. ** ** See also: file_canonical_name_dup() */ void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ blob_zero(pOut); if( file_is_absolute_path(zOrigName) ){ blob_appendf(pOut, "%/", zOrigName); }else{ char zPwd[2000]; |
︙ | ︙ | |||
1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 | zOut[0] = fossil_toupper(zOut[0]); } } #endif blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut), slash)); } /* ** The input is the name of an executable, such as one might ** type on a command-line. This routine resolves that name into ** a full pathname. The result is obtained from fossil_malloc() ** and should be freed by the caller. ** | > > > > > > > > > > > > > > > | 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 | zOut[0] = fossil_toupper(zOut[0]); } } #endif blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut), slash)); } /* ** Compute the canonical name of a file. Store that name in ** memory obtained from fossil_malloc() and return a pointer to the ** name. ** ** See also: file_canonical_name() */ char *file_canonical_name_dup(const char *zOrigName){ Blob x; if( zOrigName==0 ) return 0; blob_init(&x, 0, 0); file_canonical_name(zOrigName, &x, 0); return blob_str(&x); } /* ** The input is the name of an executable, such as one might ** type on a command-line. This routine resolves that name into ** a full pathname. The result is obtained from fossil_malloc() ** and should be freed by the caller. ** |
︙ | ︙ | |||
2378 2379 2380 2381 2382 2383 2384 | if( dryRunFlag!=0 ){ fossil_print("dry-run: would have touched %d file(s)\n", changeCount); }else{ fossil_print("Touched %d file(s)\n", changeCount); } } | > > > > > > > > > | 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 | if( dryRunFlag!=0 ){ fossil_print("dry-run: would have touched %d file(s)\n", changeCount); }else{ fossil_print("Touched %d file(s)\n", changeCount); } } /* ** If zFileName is not NULL and contains a '.', this returns a pointer ** to the position after the final '.', else it returns NULL. */ const char * file_extension(const char *zFileName){ const char * zExt = strrchr(zFileName, '.'); return zExt ? &zExt[1] : 0; } |
Changes to src/fileedit.c.
︙ | ︙ | |||
362 363 364 365 366 367 368 | ){ asDelta = 1; blob_appendf(pOut, "B %s\n", pCI->pParent->zBaseline ? pCI->pParent->zBaseline : pCI->zParentUuid); } | < < < < | 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | ){ asDelta = 1; blob_appendf(pOut, "B %s\n", pCI->pParent->zBaseline ? pCI->pParent->zBaseline : pCI->zParentUuid); } if(blob_size(&pCI->comment)!=0){ blob_appendf(pOut, "C %F\n", blob_str(&pCI->comment)); }else{ blob_append(pOut, "C (no\\scomment)\n", 16); } blob_appendf(pOut, "D %s\n", pCI->zDate); if(create_manifest_mini_fcards(pOut,pCI,asDelta,pErr)==0){ |
︙ | ︙ | |||
963 964 965 966 967 968 969 | ** Passed the values of the "checkin" and "filename" request ** properties, this function verifies that they are valid and ** populates: ** ** - *zRevUuid = the fully-expanded value of zRev (owned by the ** caller). zRevUuid may be NULL. ** | | > | > | | | > > | | | 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 | ** Passed the values of the "checkin" and "filename" request ** properties, this function verifies that they are valid and ** populates: ** ** - *zRevUuid = the fully-expanded value of zRev (owned by the ** caller). zRevUuid may be NULL. ** ** - *pVid = the RID of zRevUuid. pVid May be NULL. If the vid ** cannot be resolved or is ambiguous, pVid is not assigned. ** ** - *frid = the RID of zFilename's blob content. May not be NULL ** unless zFilename is also NULL. If BOTH of zFilename and frid are ** NULL then no confirmation is done on the filename argument - only ** zRev is checked. ** ** Returns 0 if the given file is not in the given checkin or if ** fileedit_ajax_check_filename() fails, else returns true. If it ** returns false, it queues up an error response and the caller must ** return immediately. */ static int fileedit_ajax_setup_filerev(const char * zRev, char ** zRevUuid, int * pVid, const char * zFilename, int * frid){ char * zFileUuid = 0; /* file content UUID */ const int checkFile = zFilename!=0 || frid!=0; int vid = 0; if(checkFile && !fileedit_ajax_check_filename(zFilename)){ return 0; } vid = symbolic_name_to_rid(zRev, "ci"); if(0==vid){ ajax_route_error(404,"Cannot resolve name as a checkin: %s", zRev); return 0; }else if(vid<0){ ajax_route_error(400,"Checkin name is ambiguous: %s", zRev); return 0; }else if(pVid!=0){ *pVid = vid; } if(checkFile){ zFileUuid = fileedit_file_uuid(zFilename, vid, 0); if(zFileUuid==0){ ajax_route_error(404, "Checkin does not contain file."); return 0; } } if(zRevUuid!=0){ *zRevUuid = rid_to_uuid(vid); } if(checkFile){ assert(zFileUuid!=0); if(frid!=0){ *frid = fast_uuid_to_rid(zFileUuid); } fossil_free(zFileUuid); |
︙ | ︙ | |||
1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 | p->zUser = mprintf("%s",g.zLogin); return 0; end_fail: #undef fail fossil_free(zFileUuid); return rc ? rc : 500; } /* ** AJAX route /fileedit?ajax=filelist ** ** Fetches a JSON-format list of leaves and/or filenames for use in ** creating a file selection list in /fileedit. It has different modes ** of operation depending on its arguments: | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 | p->zUser = mprintf("%s",g.zLogin); return 0; end_fail: #undef fail fossil_free(zFileUuid); return rc ? rc : 500; } /* ** Renders a list of all open leaves in JSON form: ** ** [ ** {checkin: UUID, branch: branchName, timestamp: string} ** ] ** ** The entries are ordered newest first. ** ** If zFirstUuid is not NULL then *zFirstUuid is set to a copy of the ** full UUID of the first (most recent) leaf, which must be freed by ** the caller. It is set to 0 if there are no leaves. */ static void fileedit_render_leaves_list(char ** zFirstUuid){ Blob sql = empty_blob; Stmt q = empty_Stmt; int i = 0; if(zFirstUuid){ *zFirstUuid = 0; } blob_append(&sql, timeline_query_for_tty(), -1); blob_append_sql(&sql, " AND blob.rid IN (SElECT rid FROM leaf " "WHERE NOT EXISTS(" "SELECT 1 from tagxref WHERE tagid=%d AND " "tagtype>0 AND rid=leaf.rid" ")) " "ORDER BY mtime DESC", TAG_CLOSED); db_prepare_blob(&q, &sql); CX("["); while( SQLITE_ROW==db_step(&q) ){ const char * zUuid = db_column_text(&q, 1); if(i++){ CX(","); }else if(zFirstUuid){ *zFirstUuid = fossil_strdup(zUuid); } CX("{"); CX("\"checkin\":%!j,", zUuid); CX("\"branch\":%!j,", db_column_text(&q, 7)); CX("\"timestamp\":%!j", db_column_text(&q, 2)); CX("}"); } CX("]"); db_finalize(&q); } /* ** For the given fully resolved UUID, renders a JSON object containing ** the fileeedit-editable files in that checkin: ** ** { ** checkin: UUID, ** editableFiles: [ filename1, ... filenameN ] ** } ** ** They are sorted by name using filename_collation(). */ static void fileedit_render_checkin_files(const char * zFullUuid){ Blob sql = empty_blob; Stmt q = empty_Stmt; int i = 0; CX("{\"checkin\":%!j," "\"editableFiles\":[", zFullUuid); blob_append_sql(&sql, "SELECT filename FROM files_of_checkin(%Q) " "ORDER BY filename %s", zFullUuid, filename_collation()); db_prepare_blob(&q, &sql); while( SQLITE_ROW==db_step(&q) ){ const char * zFilename = db_column_text(&q, 0); if(fileedit_is_editable(zFilename)){ if(i++){ CX(","); } CX("%!j", zFilename); } } db_finalize(&q); CX("]}"); } /* ** AJAX route /fileedit?ajax=filelist ** ** Fetches a JSON-format list of leaves and/or filenames for use in ** creating a file selection list in /fileedit. It has different modes ** of operation depending on its arguments: |
︙ | ︙ | |||
1316 1317 1318 1319 1320 1321 1322 | ** checkin: UUID, ** editableFiles: [ filename1, ... filenameN ] // sorted by name ** } ** ** On error it produces a JSON response as documented for ** ajax_route_error(). */ | | < < < < | | | < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < | 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 | ** checkin: UUID, ** editableFiles: [ filename1, ... filenameN ] // sorted by name ** } ** ** On error it produces a JSON response as documented for ** ajax_route_error(). */ static void fileedit_ajax_filelist(){ const char * zCi = PD("checkin",P("ci")); if(!ajax_route_bootstrap(1,0)){ return; } cgi_set_content_type("application/json"); if(zCi!=0){ char * zCiFull = 0; if(0==fileedit_ajax_setup_filerev(zCi, &zCiFull, 0, 0, 0)){ /* Error already reported */ return; } fileedit_render_checkin_files(zCiFull); fossil_free(zCiFull); }else if(P("leaves")!=0){ fileedit_render_leaves_list(0); }else{ ajax_route_error(500, "Unhandled URL argument."); } } /* ** AJAX route /fileedit?ajax=commit |
︙ | ︙ | |||
1482 1483 1484 1485 1486 1487 1488 | blob_reset(&manifest); CheckinMiniInfo_cleanup(&cimi); } /* ** WEBPAGE: fileedit ** | | | > > > > > | | | < > | | | > | < | < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > | 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 | blob_reset(&manifest); CheckinMiniInfo_cleanup(&cimi); } /* ** WEBPAGE: fileedit ** ** Enables the online editing and committing of text files. Requires ** that the user have Write permissions and that a user with setup ** permissions has set the fileedit-glob setting to a list of glob ** patterns matching files which may be edited (e.g. "*.wiki,*.md"). ** Note that fileedit-glob, by design, is a local-only setting. ** It does not sync across repository clones, and must be explicitly ** set on any repositories where this page should be activated. ** ** Optional query parameters: ** ** filename=FILENAME Repo-relative path to the file. ** checkin=VERSION Checkin version, using any unambiguous ** symbolic version name. ** ** If passed a filename but no checkin then it will attempt to ** load that file from the most recent leaf checkin. ** ** Once the page is loaded, files may be selected from any open leaf ** version. The only way to edit files from non-leaf checkins is to ** pass both the filename and checkin as URL parameters to the page. ** Users with the proper permissions will be presented with "Edit" ** links in various file-specific contexts for files which match the ** fileedit-glob, regardless of whether they refer to leaf versions or ** not. */ void fileedit_page(void){ const char * zFileMime = 0; /* File mime type guess */ CheckinMiniInfo cimi; /* Checkin state */ int previewRenderMode = AJAX_RENDER_GUESS; /* preview mode */ Blob err = empty_blob; /* Error report */ const char *zAjax = P("name"); /* Name of AJAX route for sub-dispatching. */ /* ** Internal-use URL parameters: ** ** name=string The name of a page-specific AJAX operation. ** ** Noting that fossil internally stores all URL path components ** after the first as the "name" value. Thus /fileedit?name=blah is ** equivalent to /fileedit/blah. The latter is the preferred ** form. This means, however, that no fileedit ajax routes may make ** use of the name parameter. ** ** Which additional parameters are used by each distinct ajax route ** is an internal implementation detail and may change with any ** given build of this code. An unknown "name" value triggers an ** error, as documented for ajax_route_error(). */ /* Allow no access to this page without check-in privilege */ login_check_credentials(); if( !g.perm.Write ){ if(zAjax!=0){ ajax_route_error(403, "Write permissions required."); }else{ login_needed(g.anon.Write); |
︙ | ︙ | |||
1588 1589 1590 1591 1592 1593 1594 | ** error in (&err) and goto end_footer instead so that we can be ** sure to emit the error message, do any cleanup, and end the ** transaction cleanly. */ { int isMissingArg = 0; if(fileedit_setup_cimi_from_p(&cimi, &err, &isMissingArg)==0){ | | < < < | 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 | ** error in (&err) and goto end_footer instead so that we can be ** sure to emit the error message, do any cleanup, and end the ** transaction cleanly. */ { int isMissingArg = 0; if(fileedit_setup_cimi_from_p(&cimi, &err, &isMissingArg)==0){ assert(cimi.zFilename); zFileMime = mimetype_from_name(cimi.zFilename); }else if(isMissingArg!=0){ /* Squelch these startup warnings - they're non-fatal now but ** used to be fatal. */ blob_reset(&err); } } |
︙ | ︙ | |||
1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 | style_emit_script_tag(1,0); /* Status bar */ CX("<div id='fossil-status-bar' " "title='Status message area. Double-click to clear them.'>" "Status messages will go here.</div>\n" /* will be moved into the tab container via JS */); /* Main tab container... */ CX("<div id='fileedit-tabs' class='tab-container'></div>"); /***** File/version info tab *****/ { CX("<div id='fileedit-tab-fileselect' " "data-tab-parent='fileedit-tabs' " | > > > > > > > > | > < < < < < | > | < | > | 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 | style_emit_script_tag(1,0); /* Status bar */ CX("<div id='fossil-status-bar' " "title='Status message area. Double-click to clear them.'>" "Status messages will go here.</div>\n" /* will be moved into the tab container via JS */); CX("<div id='fileedit-edit-status'>" "<span class='name'>(no file loaded)</span>" "<span class='links'></span>" "</div>"); /* Main tab container... */ CX("<div id='fileedit-tabs' class='tab-container'></div>"); /* The .hidden class on the following tab elements is to help lessen the FOUC effect of the tabs before JS re-assembles them. */ /***** File/version info tab *****/ { CX("<div id='fileedit-tab-fileselect' " "data-tab-parent='fileedit-tabs' " "data-tab-label='File Selection' " "class='hidden'" ">"); CX("<div id='fileedit-file-selector'></div>"); CX("</div>"/*#fileedit-tab-fileselect*/); } /******* Content tab *******/ { CX("<div id='fileedit-tab-content' " "data-tab-parent='fileedit-tabs' " "data-tab-label='File Content' " "class='hidden'" ">"); CX("<div class='flex-container flex-row child-gap-small'>"); CX("<button class='fileedit-content-reload confirmer' " "title='Reload the file from the server, discarding " "any local edits. To help avoid accidental loss of " "edits, it requires confirmation (a second click) within " "a few seconds or it will not reload.'" ">Discard & Reload</button>"); style_select_list_int("select-font-size", "editor_font_size", "Editor font size", NULL/*tooltip*/, 100, "100%", 100, "125%", 125, "150%", 150, "175%", 175, "200%", 200, NULL); CX("</div>"); CX("<div class='flex-container flex-column stretch'>"); CX("<textarea name='content' id='fileedit-content-editor' " "class='fileedit' rows='25'>"); CX("</textarea>"); CX("</div>"/*textarea wrapper*/); CX("</div>"/*#tab-file-content*/); } /****** Preview tab ******/ { CX("<div id='fileedit-tab-preview' " "data-tab-parent='fileedit-tabs' " "data-tab-label='Preview' " "class='hidden'" ">"); CX("<div class='fileedit-options flex-container flex-row'>"); CX("<button id='btn-preview-refresh' " "data-f-preview-from='fileContent' " /* ^^^ fossil.page[methodName]() OR text source elem ID, ** but we need a method in order to support clients swapping out ** the text editor with their own. */ |
︙ | ︙ | |||
1739 1740 1741 1742 1743 1744 1745 | CX("</div>"/*#fileedit-tab-preview*/); } /****** Diff tab ******/ { CX("<div id='fileedit-tab-diff' " "data-tab-parent='fileedit-tabs' " | | > | > | 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 | CX("</div>"/*#fileedit-tab-preview*/); } /****** Diff tab ******/ { CX("<div id='fileedit-tab-diff' " "data-tab-parent='fileedit-tabs' " "data-tab-label='Diff' " "class='hidden'" ">"); CX("<div class='fileedit-options flex-container " "flex-row child-gap-small' " "id='fileedit-tab-diff-buttons'>"); CX("<button class='sbs'>Side-by-side</button>" "<button class='unified'>Unified</button>"); if(0){ /* For the time being let's just ignore all whitespace ** changes, as files with Windows-style EOLs always show ** more diffs than we want then they're submitted to |
︙ | ︙ | |||
1771 1772 1773 1774 1775 1776 1777 | "</div>"); CX("</div>"/*#fileedit-tab-diff*/); } /****** Commit ******/ CX("<div id='fileedit-tab-commit' " "data-tab-parent='fileedit-tabs' " | | > | 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 | "</div>"); CX("</div>"/*#fileedit-tab-diff*/); } /****** Commit ******/ CX("<div id='fileedit-tab-commit' " "data-tab-parent='fileedit-tabs' " "data-tab-label='Commit' " "class='hidden'" ">"); { /******* Commit flags/options *******/ CX("<div class='fileedit-options flex-container flex-row'>"); style_labeled_checkbox("cb-dry-run", "dry_run", "Dry-run?", "1", 0, |
︙ | ︙ | |||
1892 1893 1894 1895 1896 1897 1898 | /* Manifest gets rendered here after a commit. */); } CX("</div>"/*#fileedit-tab-commit*/); /****** Help/Tips ******/ CX("<div id='fileedit-tab-help' " "data-tab-parent='fileedit-tabs' " | | > | 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 | /* Manifest gets rendered here after a commit. */); } CX("</div>"/*#fileedit-tab-commit*/); /****** Help/Tips ******/ CX("<div id='fileedit-tab-help' " "data-tab-parent='fileedit-tabs' " "data-tab-label='Help' " "class='hidden'" ">"); { CX("<h1>Help & Tips</h1>"); CX("<ul>"); CX("<li><strong>Only files matching the <code>fileedit-glob</code> " "repository setting</strong> can be edited online. That setting " "must be a comma- or newline-delimited list of glob patterns " |
︙ | ︙ | |||
1920 1921 1922 1923 1924 1925 1926 | "is unspecified and may differ across environments. When " "committing or force-reloading a file, local edits to that " "file/check-in combination are discarded.</li>"); CX("</ul>"); } CX("</div>"/*#fileedit-tab-help*/); | < < < < < < < < < < < < < < < < < < < < < < < < | < < < < | < < | | > | > > > > | < > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < | > > | 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 | "is unspecified and may differ across environments. When " "committing or force-reloading a file, local edits to that " "file/check-in combination are discarded.</li>"); CX("</ul>"); } CX("</div>"/*#fileedit-tab-help*/); builtin_request_js("sbsdiff.js"); style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer", "storage", 0); builtin_fulfill_js_requests(); /* ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is ** used for dynamically toggling certain UI components on and off. ** Must come after window.fossil has been intialized and before ** fossil.page.fileedit.js. Potential TODO: move this into the ** window.fossil bootstrapping so that we don't have to "fulfill" ** the JS multiple times. */ ajax_emit_js_preview_modes(1); builtin_request_js("fossil.page.fileedit.js"); builtin_fulfill_js_requests(); { /* Dynamically populate the editor, display any error in the err ** blob, and/or switch to tab #0, where the file selector ** lives. The extra C scopes here correspond to JS-level scopes, ** to improve grokability. */ style_emit_script_tag(0,0); CX("\n(function(){\n"); CX("try{\n"); { char * zFirstLeafUuid = 0; CX("fossil.config['fileedit-glob'] = "); glob_render_json_to_cgi(fileedit_glob()); CX(";\n"); if(blob_size(&err)>0){ CX("fossil.error(%!j);\n", blob_str(&err)); } /* Populate the page with the current leaves and, if available, the selected checkin's file list, to save 1 or 2 XHR requests at startup. That makes this page uncacheable, but compressed delivery of this page is currently less than 6k. */ CX("fossil.page.initialLeaves = "); fileedit_render_leaves_list(cimi.zParentUuid ? 0 : &zFirstLeafUuid); CX(";\n"); if(zFirstLeafUuid){ assert(!cimi.zParentUuid); cimi.zParentUuid = zFirstLeafUuid; zFirstLeafUuid = 0; } if(cimi.zParentUuid){ CX("fossil.page.initialFiles = "); fileedit_render_checkin_files(cimi.zParentUuid); CX(";\n"); } CX("fossil.onPageLoad(function(){\n"); { if(blob_size(&err)>0){ CX("fossil.error(%!j);\n", blob_str(&err)); CX("fossil.page.tabs.switchToTab(0);\n"); } if(cimi.zParentUuid && cimi.zFilename){ CX("fossil.page.loadFile(%!j,%!j);\n", cimi.zFilename, cimi.zParentUuid) /* Reminder we cannot embed the JSON-format content of the file here because if it contains a SCRIPT tag then it will break the whole page. */; } } CX("});\n")/*fossil.onPageLoad()*/; } CX("}catch(e){" "fossil.error(e); console.error('Exception:',e);" "}\n"); CX("})();")/*anonymous function*/; style_emit_script_tag(1,0); } blob_reset(&err); CheckinMiniInfo_cleanup(&cimi); db_end_transaction(0); style_footer(); } |
Changes to src/finfo.c.
︙ | ︙ | |||
52 53 54 55 56 57 58 | ** -r|--revision R print the given revision (or ckout, if none is given) ** to stdout (only in print mode) ** -s|--status select status mode (print a status indicator for FILE) ** -W|--width <num> Width of lines (default is to auto-detect). Must be ** >22 or 0 (= no limit, resulting in a single line per ** entry). ** | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | ** -r|--revision R print the given revision (or ckout, if none is given) ** to stdout (only in print mode) ** -s|--status select status mode (print a status indicator for FILE) ** -W|--width <num> Width of lines (default is to auto-detect). Must be ** >22 or 0 (= no limit, resulting in a single line per ** entry). ** ** See also: [[artifact]], [[cat]], [[descendants]], [[info]], [[leaves]] */ void finfo_cmd(void){ db_must_be_within_tree(); if( find_option("status","s",0) ){ Stmt q; Blob line; Blob fname; |
︙ | ︙ | |||
243 244 245 246 247 248 249 | ** in the repository. The version currently checked out is shown by default. ** Other versions may be specified using the -r option. ** ** Options: ** -R|--repository FILE Extract artifacts from repository FILE ** -r VERSION The specific check-in containing the file ** | | | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | ** in the repository. The version currently checked out is shown by default. ** Other versions may be specified using the -r option. ** ** Options: ** -R|--repository FILE Extract artifacts from repository FILE ** -r VERSION The specific check-in containing the file ** ** See also: [[finfo]] */ void cat_cmd(void){ int i; Blob content, fname; const char *zRev; db_find_and_open_repository(0, 0); zRev = find_option("r","r",1); |
︙ | ︙ |
Changes to src/forum.c.
︙ | ︙ | |||
747 748 749 750 751 752 753 | @ </div> } forumthread_delete(pThread); return target; } /* | < < < < | | < | > | | | 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 | @ </div> } forumthread_delete(pThread); return target; } /* ** Emits all JS code required by /forumpost. */ static void forumpost_emit_page_js(){ static int once = 0; if(0==once){ once = 1; style_emit_script_fossil_bootstrap(1); builtin_request_js("forum.js"); builtin_request_js("fossil.dom.js"); builtin_request_js("fossil.page.forumpost.js"); } } /* ** WEBPAGE: forumpost ** ** Show a single forum posting. The posting is shown in context with |
︙ | ︙ | |||
892 893 894 895 896 897 898 | style_submenu_element("Complete Thread", "%R/%s/%s?t=a", g.zPath, zName); forum_display_history(froot, fpid, 1); }else{ style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName); forum_display_hierarchical(froot, fpid); } | | | 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 | style_submenu_element("Complete Thread", "%R/%s/%s?t=a", g.zPath, zName); forum_display_history(froot, fpid, 1); }else{ style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName); forum_display_hierarchical(froot, fpid); } forumpost_emit_page_js(); style_footer(); } /* ** Return true if a forum post should be moderated. */ static int forum_need_moderation(void){ |
︙ | ︙ |
Changes to src/forum.js.
︙ | ︙ | |||
12 13 14 15 16 17 18 | if(x[0]){ var w = window.innerHeight; var h = x[0].scrollHeight; var y = absoluteY(x[0]); if( w>h ) y = y + (h-w)/2; if( y>0 ) window.scrollTo(0, y); } | | | 12 13 14 15 16 17 18 19 | if(x[0]){ var w = window.innerHeight; var h = x[0].scrollHeight; var y = absoluteY(x[0]); if( w>h ) y = y + (h-w)/2; if( y>0 ) window.scrollTo(0, y); } })(); |
Changes to src/fossil.bootstrap.js.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | "use strict"; (function(global){ /* Bootstrapping bits for the global.fossil object. Must be loaded after style.c:style_emit_script_tag() has initialized that object. */ const F = global.fossil; /** Returns the current time in something approximating ISO-8601 format. */ const timestring = function f(){ if(!f.rx1){ f.rx1 = /\.\d+Z$/; } const d = new Date(); return d.toISOString().replace(f.rx1,'').split('T').join(' '); }; /* ** By default fossil.message() sends its arguments console.debug(). If ** fossil.message.targetElement is set, it is assumed to be a DOM ** element, its innerText gets assigned to the concatenation of all ** arguments (with a space between each), and the CSS 'error' class is ** removed from the object. Pass it a falsy value to clear the target ** element. ** ** Returns this object. */ F.message = function f(msg){ const args = Array.prototype.slice.call(arguments,0); const tgt = f.targetElement; | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | "use strict"; (function () { /* CustomEvent polyfill, courtesy of Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent */ if(typeof window.CustomEvent === "function") return false; window.CustomEvent = function(event, params) { if(!params) params = {bubbles: false, cancelable: false, detail: null}; const evt = document.createEvent('CustomEvent'); evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail ); return evt; }; })(); (function(global){ /* Bootstrapping bits for the global.fossil object. Must be loaded after style.c:style_emit_script_tag() has initialized that object. */ const F = global.fossil; /** Returns the current time in something approximating ISO-8601 format. */ const timestring = function f(){ if(!f.rx1){ f.rx1 = /\.\d+Z$/; } const d = new Date(); return d.toISOString().replace(f.rx1,'').split('T').join(' '); }; /** Returns the local time string of Date object d, defaulting to the current time. */ const localTimeString = function ff(d){ if(!ff.pad){ ff.pad = (x)=>(''+x).length>1 ? x : '0'+x; } d || (d = new Date()); return [ d.getFullYear(),'-',ff.pad(d.getMonth()+1/*sigh*/), '-',ff.pad(d.getDate()), ' ',ff.pad(d.getHours()),':',ff.pad(d.getMinutes()), ':',ff.pad(d.getSeconds()) ].join(''); }; /* ** By default fossil.message() sends its arguments console.debug(). If ** fossil.message.targetElement is set, it is assumed to be a DOM ** element, its innerText gets assigned to the concatenation of all ** arguments (with a space between each), and the CSS 'error' class is ** removed from the object. Pass it a falsy value to clear the target ** element. ** ** Returns this object. */ F.message = function f(msg){ const args = Array.prototype.slice.call(arguments,0); const tgt = f.targetElement; if(args.length) args.unshift( localTimeString()+':' //timestring(),'UTC:' ); if(tgt){ tgt.classList.remove('error'); tgt.innerText = args.join(' '); } else{ if(args.length){ args.unshift('Fossil status:'); |
︙ | ︙ | |||
105 106 107 108 109 110 111 | }; /** repoUrl( repoRelativePath [,urlParams] ) Creates a URL by prepending this.rootPath to the given path (which must be relative from the top of the site, without a leading slash). If urlParams is a string, it must be | | | | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | }; /** repoUrl( repoRelativePath [,urlParams] ) Creates a URL by prepending this.rootPath to the given path (which must be relative from the top of the site, without a leading slash). If urlParams is a string, it must be paramters encoded in the form "key=val&key2=val2..." WITHOUT a leading '?'. If it's an object, all of its properties get appended to the URL in that form. */ F.repoUrl = function(path,urlParams){ if(!urlParams) return this.rootPath+path; const url=[this.rootPath,path]; url.push('?'); if('string'===typeof urlParams) url.push(urlParams); else if(urlParams && 'object'===typeof urlParams){ this.encodeUrlArgs(urlParams, url); } return url.join(''); }; /** Returns true if v appears to be a plain object. |
︙ | ︙ |
Changes to src/fossil.confirmer.js.
︙ | ︙ | |||
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | element. On each subsequent call, the tick count will be reduced by 1, and it is passed 0 after the final tick expires or when the action has been confirmed, immediately before the onconfirm or ontimeout callback. The intention of the callback is to update the label of the target element. If .ticks is set but .ontick is not then a default implementation is used which updates the element with the .confirmText, prepending a countdown to it. .debug = boolean. If truthy, it sends some debug output to the dev console to track what it's doing. Various notes: - To change the default option values, modify the fossil.confirmer.defaultOpts object. - Exceptions triggered via the callbacks are caught and emitted to the dev console if the debug option is enabled, but are otherwise ignored. - Due to the nature of multi-threaded code, it is potentially possible that confirmation and timeout actions BOTH happen if the user triggers the associated action at "just the right millisecond" before the timeout is triggered. | > > > > > > > > > > > > > | | | | > > > > > > > | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | element. On each subsequent call, the tick count will be reduced by 1, and it is passed 0 after the final tick expires or when the action has been confirmed, immediately before the onconfirm or ontimeout callback. The intention of the callback is to update the label of the target element. If .ticks is set but .ontick is not then a default implementation is used which updates the element with the .confirmText, prepending a countdown to it. .pinSize = if true AND confirmText is set, calculate the larger of the element's original and confirmed size and pin it to the larger of those sizes to avoid layout reflows when confirmation is running. The pinning is implemented by setting its minWidth and maxWidth style properties to the same value. This does not work if the element text is updated dynamically via ontick(). This ONLY works if the element is in the DOM and is not hidden (e.g. via display:none) at the time this routine is called, otherwise we cannot calculate its size. If the element needs to be hidden, hide it after initializing the confirmer. .debug = boolean. If truthy, it sends some debug output to the dev console to track what it's doing. Various notes: - To change the default option values, modify the fossil.confirmer.defaultOpts object. - Exceptions triggered via the callbacks are caught and emitted to the dev console if the debug option is enabled, but are otherwise ignored. - Due to the nature of multi-threaded code, it is potentially possible that confirmation and timeout actions BOTH happen if the user triggers the associated action at "just the right millisecond" before the timeout is triggered. TODO: - Add an invert option which activates if the timeout is reached and "times out" if the element is clicked again. e.g. a button which says "Saving..." and cancels the op if it's clicked again, else it saves after X time/ticks. - Internally we save/restore the initial text of non-INPUT elements using innerHTML. We should instead move their child nodes aside (into an internal out-of-DOM element) and restore them as needed. Terse Change history: - 20200811 - Added pinSize option. - 20200507: - Add a tick-based countdown in order to more easily support updating the target element with the countdown. - 20200506: - Ported from jQuery to plain JS. |
︙ | ︙ | |||
136 137 138 139 140 141 142 143 144 145 | this.timerID = undefined; this.state = this.states.initial; const isInput = f.isInput(target); const updateText = function(msg){ if(isInput) target.value = msg; else target.innerHTML = msg; } updateText(this.opt.initialText); if(this.opt.ticks && !this.opt.ontick){ this.opt.ontick = function(tick){ | > > > > > > > > > > > > | | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | this.timerID = undefined; this.state = this.states.initial; const isInput = f.isInput(target); const updateText = function(msg){ if(isInput) target.value = msg; else target.innerHTML = msg; } const formatCountdown = (txt, number) => txt + " ["+number+"]"; if(opt.pinSize && opt.confirmText){ /* Try to pin the element's width the the greater of its current width or its waiting-on-confirmation width to avoid layout reflow when it's activated. */ const digits = (''+(opt.timeout/1000 || opt.ticks)).length; const lblLong = formatCountdown(opt.confirmText, "00000000".substr(0,digits)); const w1 = parseFloat(window.getComputedStyle(target).width); updateText(lblLong); const w2 = parseFloat(window.getComputedStyle(target).width); target.style.minWidth = target.style.maxWidth = (w1>w2 ? w1 : w2)+"px"; } updateText(this.opt.initialText); if(this.opt.ticks && !this.opt.ontick){ this.opt.ontick = function(tick){ updateText(formatCountdown(self.opt.confirmText,tick)); }; } this.setClasses(false); this.doTimeout = function() { if(this.timerID){ clearTimeout( this.timerID ); delete this.timerID; |
︙ | ︙ | |||
266 267 268 269 270 271 272 | new f.Holder(elem,opt); return this; }; /** The default options for initConfirmer(). Tweak them to set the defaults. A couple of them (initialText and confirmText) are dynamically-generated, and can't reasonably be set in the | | > | | | 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | new f.Holder(elem,opt); return this; }; /** The default options for initConfirmer(). Tweak them to set the defaults. A couple of them (initialText and confirmText) are dynamically-generated, and can't reasonably be set in the defaults. Some, like ticks, cannot be set here because that would end up indirectly replacing non-tick timeouts with ticks. */ F.confirmer.defaultOpts = { timeout:undefined, ticks: 3, ticktime: 998/*not *quite* 1000*/, onconfirm: undefined, ontimeout: undefined, onactivate: undefined, classInitial: '', classWaiting: '', debug: false }; })(window.fossil); |
Added src/fossil.copybutton.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | (function(F/*fossil object*/){ /** A basic API for creating and managing a copy-to-clipboard button. Requires: fossil.bootstrap, fossil.dom */ const D = F.dom; /** Initializes element e as a copy button using the given options object. The first argument may be a DOM element or a string (CSS selector suitable for use with document.querySelector()). Options: .copyFromElement: DOM element .copyFromId: DOM element ID One of copyFromElement or copyFromId must be provided, but copyFromId may optionally be provided via e.dataset.copyFromId. .extractText: optional callback which is triggered when the copy button is clicked. I tmust return the text to copy to the clipboard. The default is to extract it from the copy-from element, using its [value] member, if it has one, else its [innerText]. A client-provided callback may use any data source it likes, so long as it's synchronous. If this function returns a falsy value then the clipboard is not modified. This function is called with the fully expanded/resolved options object as its "this" (that's a different instance than the one passed to this function!). .cssClass: optional CSS class, or list of classes, to apply to e. .style: optional object of properties to copy directly into e.style. .oncopy: an optional callback function which is added as an event listener for the 'text-copied' event (see below). There is functionally no difference from setting this option or adding a 'text-copied' event listener to the element, and this option is considered to be a convenience form of that. Note that this function's own defaultOptions object holds default values for some options. Any changes made to that object affect any future calls to this function. Be aware that clipboard functionality might or might not be available in any given environment. If this button appears to have no effect, that may be because it is not enabled/available in the current platform. The copy button emits custom event 'text-copied' after it has successfully copied text to the clipboard. The event's "detail" member is an object with a "text" property holding the copied text. Other properties may be added in the future. The event is not fired if copying to the clipboard fails (e.g. is not available in the current environment). Returns the copy-initialized element. Example: const button = fossil.copyButton('#my-copy-button', { copyFromId: 'some-other-element-id' }); button.addEventListener('text-copied',function(ev){ fossil.dom.flashOnce(ev.target); console.debug("Copied text:",ev.detail.text); }); */ F.copyButton = function f(e, opt){ if('string'===typeof e){ e = document.querySelector(e); } opt = F.mergeLastWins(f.defaultOptions, opt); if(opt.cssClass){ D.addClass(e, opt.cssClass); } var srcId, srcElem; if(opt.copyFromElement){ srcElem = opt.copyFromElement; }else if((srcId = opt.copyFromId || e.dataset.copyFromId)){ srcElem = document.querySelector('#'+srcId); } const extract = opt.extractText || ( undefined===srcElem.value ? ()=>srcElem.innerText : ()=>srcElem.value ); D.copyStyle(e, opt.style); e.addEventListener( 'click', function(){ const txt = extract.call(opt); if(txt && D.copyTextToClipboard(txt)){ e.dispatchEvent(new CustomEvent('text-copied',{ detail: {text: txt} })); } }, false ); if('function' === typeof opt.oncopy){ e.addEventListener('text-copied', opt.oncopy, false); } return e; }; F.copyButton.defaultOptions = { cssClass: 'copy-button', style: {/*properties copied as-is into element.style*/} }; })(window.fossil); |
Changes to src/fossil.dom.js.
︙ | ︙ | |||
463 464 465 466 467 468 469 470 471 472 473 474 475 | dom.selectOne = function(x,origin){ var src = origin || document, e = src.querySelector(x); if(!e){ e = new Error("Cannot find DOM element: "+x); console.error(e, src); throw e; } return e; }; return F.dom = dom; })(window.fossil); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 | dom.selectOne = function(x,origin){ var src = origin || document, e = src.querySelector(x); if(!e){ e = new Error("Cannot find DOM element: "+x); console.error(e, src); throw e; } return e; }; /** "Blinks" the given element a single time for the given number of milliseconds, defaulting (if the 2nd argument is falsy or not a number) to flashOnce.defaultTimeMs. If a 3rd argument is passed in, it must be a function, and it gets callback back at the end of the asynchronous flashing processes. This will only activate once per element during that timeframe - further calls will become no-ops until the blink is completed. This routine adds a dataset member to the element for the duration of the blink, to allow it to block multiple blinks. If passed 2 arguments and the 2nd is a function, it behaves as if it were called as (arg1, undefined, arg2). Returns e, noting that the flash itself is asynchronous and may still be running, or not yet started, when this function returns. */ dom.flashOnce = function f(e,howLongMs,afterFlashCallback){ if(e.dataset.isBlinking){ return; } if(2===arguments.length && 'function' ===typeof howLongMs){ afterFlashCallback = howLongMs; howLongMs = f.defaultTimeMs; } if(!howLongMs || 'number'!==typeof howLongMs){ howLongMs = f.defaultTimeMs; } e.dataset.isBlinking = true; const transition = e.style.transition; e.style.transition = "opacity "+howLongMs+"ms ease-in-out"; const opacity = e.style.opacity; e.style.opacity = 0; setTimeout(function(){ e.style.transition = transition; e.style.opacity = opacity; delete e.dataset.isBlinking; if(afterFlashCallback) afterFlashCallback(); }, howLongMs); return e; }; dom.flashOnce.defaultTimeMs = 400; /** Attempts to copy the given text to the system clipboard. Returns true if it succeeds, else false. */ dom.copyTextToClipboard = function(text){ if( window.clipboardData && window.clipboardData.setData ){ clipboardData.setData('Text',text); return true; }else{ const x = document.createElement("textarea"); x.style.position = 'fixed'; x.value = text; document.body.appendChild(x); x.select(); var rc; try{ document.execCommand('copy'); rc = true; }catch(err){ rc = false; }finally{ document.body.removeChild(x); } return rc; } }; /** Copies all properties from the 2nd argument (a plain object) into the style member of the first argument (DOM element or a forEach-capable list of elements). If the 2nd argument is falsy or empty, this is a no-op. Returns its first argument. */ dom.copyStyle = function f(e, style){ if(e.forEach){ e.forEach((x)=>f(x, style)); return e; } if(style){ let k; for(k in style){ if(style.hasOwnProperty(k)) e.style[k] = style[k]; } } return e; }; return F.dom = dom; })(window.fossil); |
Changes to src/fossil.fetch.js.
1 2 3 | "use strict"; /** Requires that window.fossil has already been set up. | | > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | "use strict"; /** Requires that window.fossil has already been set up. */ (function(namespace){ const fossil = namespace; /** fetch() is an HTTP request/response mini-framework similar (but not identical) to the not-quite-ubiquitous window.fetch(). JS usages: fossil.fetch( URI [, onLoadCallback] ); |
︙ | ︙ | |||
85 86 87 88 89 90 91 | onload/onerror/beforesend/aftersend handlers of its own, this function falls to defaults which are member properties of this function with the same name, e.g. fossil.fetch.onload(). The default onload/onerror implementations route the data through the dev console and (for onerror()) through fossil.error(). The default beforesend/aftersend are no-ops. Individual pages may overwrite those members to provide default implementations suitable for the | | > > | < > | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | onload/onerror/beforesend/aftersend handlers of its own, this function falls to defaults which are member properties of this function with the same name, e.g. fossil.fetch.onload(). The default onload/onerror implementations route the data through the dev console and (for onerror()) through fossil.error(). The default beforesend/aftersend are no-ops. Individual pages may overwrite those members to provide default implementations suitable for the page's use, e.g. keeping track of how many in-flight ajax requests are pending. Any exceptions thrown in an beforesend/aftersend handler are current ignored (feature or bug?). Note that this routine may add properties to the 2nd argument, so that instance should not be kept around for later use. Returns this object, noting that the XHR request is asynchronous, and still in transit (or has yet to be sent) when that happens. */ fossil.fetch = function f(uri,opt){ const F = fossil; if(!f.onload){ f.onload = (r)=>console.debug('fossil.fetch() XHR response:',r); } if(!f.onerror){ f.onerror = function(e/*exception*/){ console.error("fossil.fetch() XHR error:",e); if(e instanceof Error) F.error('Exception:',e); else F.error("Unknown error in handling of XHR request."); }; } if(!f.parseResponseHeaders){ f.parseResponseHeaders = function(h){ const rc = {}; if(!h) return rc; const ar = h.trim().split(/[\r\n]+/); ar.forEach(function(line) { const parts = line.split(': '); |
︙ | ︙ | |||
140 141 142 143 144 145 146 | && !(payload instanceof ArrayBuffer) && ('object'===typeof payload || payload instanceof Array)){ payload = JSON.stringify(payload); opt.contentType = 'application/json'; } } | | | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | && !(payload instanceof ArrayBuffer) && ('object'===typeof payload || payload instanceof Array)){ payload = JSON.stringify(payload); opt.contentType = 'application/json'; } } const url=[f.urlTransform(uri,opt.urlParams)], x=new XMLHttpRequest(); if('json'===opt.responseType){ /* 'json' is an extension to the supported XHR.responseType list. We use it as a flag to tell us to JSON.parse() the response. */ jsonResponse = true; x.responseType = 'text'; |
︙ | ︙ | |||
199 200 201 202 203 204 205 | } x.timeout = +opt.timeout || f.timeout; if(undefined!==payload) x.send(payload); else x.send(); return this; }; | > > > > > > > > | | | > | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | } x.timeout = +opt.timeout || f.timeout; if(undefined!==payload) x.send(payload); else x.send(); return this; }; /** urlTransform() must refer to a function which accepts a relative path to the same site as fetch() is served from and an optional set of URL parameters to pass with it (in the form a of a string ("a=b&c=d...") or an object of key/value pairs (which it converts to such a string), and returns the resulting URL or URI as a string. */ fossil.fetch.urlTransform = (u,p)=>fossil.repoUrl(u,p); fossil.fetch.beforesend = function(){}; fossil.fetch.aftersend = function(){}; fossil.fetch.timeout = 15000/* Default timeout, in ms. */; })(window.fossil); |
Added src/fossil.numbered-lines.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | (function callee(arg){ /* JS counterpart of info.c:output_text_with_line_numbers() which ties an event handler to the line numbers to allow selection of individual lines or ranges. Requires: fossil.bootstrap, fossil.dom, fossil.popupwidget, fossil.copybutton */ var tbl = arg || document.querySelectorAll('table.numbered-lines'); if(!tbl) return /* no matching elements */; else if(!arg){ if(tbl.length>1){ /* multiple query results: recurse */ tbl.forEach( (t)=>callee(t) ); return; }else{/* single query result */ tbl = tbl[0]; } } const F = window.fossil, D = F.dom; const tdLn = tbl.querySelector('td.line-numbers'); const lineState = { urlArgs: (window.location.search||'?').replace(/&?\bln=[^&]*/,''), start: 0, end: 0 }; const lineTip = new fossil.PopupWidget({ style: { cursor: 'pointer' }, refresh: function(){ const link = this.state.link; D.clearElement(link); if(lineState.start){ const ls = [lineState.start]; if(lineState.end) ls.push(lineState.end); link.dataset.url = ( window.location.toString().split('?')[0] + lineState.urlArgs + '&ln='+ls.join('-') ); D.append( D.clearElement(link), ' ', (ls.length===1 ? 'line ' : 'lines ')+ls.join('-') ); }else{ D.append(link, "No lines selected."); } }, init: function(){ const e = this.e; const btnCopy = D.span(), link = D.span(); this.state = {link}; F.copyButton(btnCopy,{ copyFromElement: link, extractText: ()=>link.dataset.url, oncopy: (ev)=>{ D.flashOnce(ev.target, undefined, ()=>lineTip.hide()); F.toast("Copied link to clipboard."); } }); this.e.addEventListener('click', ()=>btnCopy.click(), false); D.append(this.e, btnCopy, link) } }); tbl.addEventListener('click', ()=>lineTip.hide(), true); tdLn.addEventListener('click', function f(ev){ if('SPAN'!==ev.target.tagName) return; else if('number' !== typeof f.mode){ f.mode = 0 /*0=none selected, 1=1 selected, 2=2 selected*/; f.spans = tdLn.querySelectorAll('span'); f.selected = tdLn.querySelectorAll('span.selected-line'); f.unselect = (e)=>D.removeClass(e, 'selected-line','start','end'); } ev.stopPropagation(); const ln = +ev.target.innerText; if(2===f.mode){/*Reset selection*/ f.mode = 0; } if(0===f.mode){/*Select single line*/ lineState.end = 0; lineState.start = ln; f.mode = 1; }else if(1===f.mode){ if(ln === lineState.start){/*Unselect line*/ lineState.start = 0; f.mode = 0; }else{/*Select range*/ if(ln<lineState.start){ lineState.end = lineState.start; lineState.start = ln; }else{ lineState.end = ln; } f.mode = 2; } } if(f.selected){/*Unmark previously-selected lines.*/ f.selected.forEach(f.unselect); f.selected = undefined; } if(0===f.mode){ lineTip.hide(); }else{/*Mark selected lines*/ const rect = ev.target.getBoundingClientRect(); f.selected = []; if(f.spans.length>=lineState.start){ let i = lineState.start, end = lineState.end || lineState.start, span = f.spans[i-1]; for( ; i<=end && span; span = f.spans[i++] ){ span.classList.add('selected-line'); f.selected.push(span); if(i===lineState.start) span.classList.add('start'); if(i===end) span.classList.add('end'); } } lineTip.refresh().show(rect.right+3, rect.top-4); } }, false); })(); |
Changes to src/fossil.page.fileedit.js.
︙ | ︙ | |||
119 120 121 122 123 124 125 | those files' contents because otherwise we would be required to JSONize/de-JSONize the file content when stashing/restoring it, and that would be horribly inefficient (meaning "battery-consuming" on mobile devices). */ const $stash = { keys: { | | | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | those files' contents because otherwise we would be required to JSONize/de-JSONize the file content when stashing/restoring it, and that would be horribly inefficient (meaning "battery-consuming" on mobile devices). */ const $stash = { keys: { index: F.page.name+'.index' }, /** index: { "CHECKIN_HASH:FILENAME": {file info w/o content} ... } |
︙ | ︙ | |||
147 148 149 150 151 152 153 | by prepending P.name to suffix. */ contentKey: function(suffix){return P.name+'/'+suffix}, /** Returns the index object, fetching it from the stash or creating it anew on the first call. */ getIndex: function(){ if(!this.index){ this.index = F.storage.getJSON( | | < < < < < < < < < < < < < < < | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | by prepending P.name to suffix. */ contentKey: function(suffix){return P.name+'/'+suffix}, /** Returns the index object, fetching it from the stash or creating it anew on the first call. */ getIndex: function(){ if(!this.index){ this.index = F.storage.getJSON( this.keys.index, {} ); } return this.index; }, _fireStashEvent: function(){ if(this._disableNextEvent) delete this._disableNextEvent; else F.page.dispatchEvent('fileedit-stash-updated', this); }, |
︙ | ︙ | |||
298 299 300 301 302 303 304 | D.append(D.clearElement( this.e.ciListLabel, this.e.selectCi, this.e.selectFiles ),"Loading leaves..."); D.disable(this.e.btnLoadFile, this.e.selectFiles, this.e.selectCi); const self = this; | < < < | | | | | > | > > > | | | | | | | | > > > | | > > > > > > > > > | > | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | D.append(D.clearElement( this.e.ciListLabel, this.e.selectCi, this.e.selectFiles ),"Loading leaves..."); D.disable(this.e.btnLoadFile, this.e.selectFiles, this.e.selectCi); const self = this; const onload = function(list){ D.append(D.clearElement(self.e.ciListLabel), "Open leaves (newest first):"); self.cache.checkins = list; D.clearElement(D.enable(self.e.selectCi)); let loadThisOne = P.initialFiles/*possibly injected at page-load time*/; if(loadThisOne){ self.cache.files[loadThisOne.checkin] = loadThisOne; delete P.initialFiles; } list.forEach(function(o,n){ if(!n && !loadThisOne) loadThisOne = o; self.cache.branchNames[F.hashDigits(o.checkin,true)] = o.branch; D.option(self.e.selectCi, o.checkin, o.timestamp+' ['+o.branch+']: ' +F.hashDigits(o.checkin)); }); F.storage.setJSON(self.cache.branchKey, self.cache.branchNames); if(loadThisOne){ self.e.selectCi.value = loadThisOne.checkin; } self.loadFiles(loadThisOne ? loadThisOne.checkin : false); }; if(P.initialLeaves/*injected at page-load time.*/){ const lv = P.initialLeaves; delete P.initialLeaves; onload(lv); }else{ F.fetch('fileedit/filelist',{ urlParams:'leaves', responseType: 'json', onload: onload }); } }, /** Loads the file list for the given checkin UUID. It uses a cached copy on subsequent calls for the same UUID. If passed a falsy value, it instead clears and disables the file selection list. */ |
︙ | ︙ | |||
385 386 387 388 389 390 391 | /** Initializes the checkin/file selector widget. Must only be called once. */ init: function(){ this.cache.branchNames = F.storage.getJSON(this.cache.branchKey, {}); | | | | > < | > > > > > > > > > > > > > < | | | | | > | | | < < < < < > > > > > > > > > > > > > > > > | 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 | /** Initializes the checkin/file selector widget. Must only be called once. */ init: function(){ this.cache.branchNames = F.storage.getJSON(this.cache.branchKey, {}); const selCi = this.e.selectCi = D.addClass(D.select(), 'flex-grow'), selFiles = this.e.selectFiles = D.addClass(D.select(), 'file-list'), btnLoad = this.e.btnLoadFile = D.addClass(D.button("Load file"), "flex-shrink"), filesLabel = this.e.fileListLabel = D.addClass(D.div(),'flex-shrink','file-list-label'), ciLabelWrapper = D.addClass( D.div(), 'flex-container','flex-row', 'flex-shrink', 'stretch', 'child-gap-small' ), btnReload = D.addClass( D.button('Reload'), 'flex-shrink' ), ciLabel = this.e.ciListLabel = D.addClass(D.span(),'flex-shrink','checkin-list-label') ; D.attr(selCi, 'title',"The list of opened leaves."); D.attr(selFiles, 'title', "The list of editable files for the selected checkin."); D.attr(btnLoad, 'title', "Load the selected file into the editor."); D.disable(selCi, selFiles, btnLoad); D.attr(selFiles, 'size', 12); D.append( this.e.container, ciLabel, D.append(ciLabelWrapper, selCi, btnReload), filesLabel, selFiles, /* Use a wrapper for btnLoad so that the button itself does not stretch to fill the parent width: */ D.append(D.addClass(D.div(), 'flex-shrink'), btnLoad) ); if(F.config['fileedit-glob']){ D.append( this.e.container, D.append( D.span(), D.append(D.code(),"fileedit-glob"), " config setting = ", D.append(D.code(), JSON.stringify(F.config['fileedit-glob'])) ) ); } this.loadLeaves(); selCi.addEventListener( 'change', (e)=>this.loadFiles(e.target.value), false ); const doLoad = (e)=>{ this.finfo.filename = selFiles.value; if(this.finfo.filename){ P.loadFile(this.finfo.filename, this.finfo.checkin); } }; btnLoad.addEventListener('click', doLoad, false); selFiles.addEventListener('dblclick', doLoad, false); btnReload.addEventListener( 'click', (e)=>this.loadLeaves(), false ); delete this.init; } }/*P.fileSelectWidget*/; /** Widget for listing and selecting $stash entries. */ P.stashWidget = { e:{/*DOM element(s)*/}, init: function(domInsertPoint/*insert widget BEFORE this element*/){ const wrapper = D.addClass( D.attr(D.div(),'id','fileedit-stash-selector'), 'input-with-label' ); const sel = this.e.select = D.select(); const btnClear = this.e.btnClear = D.button("Discard Edits"); D.append(wrapper, "Local edits (", D.append(D.code(), F.storage.storageImplName()), "):", sel, btnClear); D.attr(wrapper, "title", [ 'Locally-edited files. Timestamps are the last local edit time.', 'Only the',P.config.defaultMaxStashSize,'most recent checkin/file', 'combinations are retained.', 'Committing or reloading a file removes it from this list.' ].join(' ')); D.option(D.disable(sel), "(empty)"); F.page.addEventListener('fileedit-stash-updated',(e)=>this.updateList(e.detail)); F.page.addEventListener('fileedit-file-loaded',(e)=>this.updateList($stash, e.detail)); sel.addEventListener('change',function(e){ const opt = this.selectedOptions[0]; if(opt && opt._finfo) P.loadFile(opt._finfo); }); if(F.storage.isTransient()){/*Warn if our storage is particularly transient...*/ D.append(wrapper, D.append( D.addClass(D.span(),'warning'), "Warning: persistent storage is not available, "+ "so uncomitted edits will not survive a page reload." )); } domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint); F.confirmer(btnClear, { /* must come after insertion into the DOM for the pinSize option to work. */ pinSize: true, confirmText: "DISCARD all local edits?", onconfirm: function(e){ if(P.finfo){ const stashed = P.getStashedFinfo(P.finfo); P.clearStash(); if(stashed) P.loadFile(/*reload after discarding edits*/); }else{ P.clearStash(); } }, ticks: F.config.confirmerButtonTicks }); D.addClass(this.e.btnClear,'hidden' /* must not be set until after confirmer is set up!*/); $stash._fireStashEvent(/*read the page-load-time stash*/); delete this.init; }, /** Regenerates the edit selection list. */ updateList: function f(stasher,theFinfo){ |
︙ | ︙ | |||
519 520 521 522 523 524 525 | D.addClass(this.e.btnClear, 'hidden'); D.option(D.disable(this.e.select),"No local edits"); return; } D.enable(this.e.select); D.removeClass(this.e.btnClear, 'hidden'); D.disable(D.option(this.e.select,0,"Select a local edit...")); | | | | 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 | D.addClass(this.e.btnClear, 'hidden'); D.option(D.disable(this.e.select),"No local edits"); return; } D.enable(this.e.select); D.removeClass(this.e.btnClear, 'hidden'); D.disable(D.option(this.e.select,0,"Select a local edit...")); const currentFinfo = theFinfo || P.finfo || {filename:''}; ilist.sort(f.compare).forEach(function(finfo,n){ const key = stasher.indexKey(finfo), branch = finfo.branch || P.fileSelectWidget.checkinBranchName(finfo.checkin)||''; /* Remember that we don't know the branch name for non-leaf versions which P.fileSelectWidget() has never seen/cached. */ const opt = D.option( self.e.select, n+1/*value is (almost) irrelevant*/, [F.hashDigits(finfo.checkin), ' [',branch||'?branch?','] ', f.timestring(new Date(finfo.stashTime)),' ', false ? finfo.filename : F.shortenFilename(finfo.filename) ].join('') ); opt._finfo = finfo; if(0===f.compare(currentFinfo, finfo)){ D.attr(opt, 'selected', true); |
︙ | ︙ | |||
635 636 637 638 639 640 641 | cbLineNumbersWrap: E('#cb-line-numbers'), cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'), previewTarget: E('#fileedit-tab-preview-wrapper'), manifestTarget: E('#fileedit-manifest'), diffTarget: E('#fileedit-tab-diff-wrapper'), cbIsExe: E('input[type=checkbox][name=exec_bit]'), cbManifest: E('input[type=checkbox][name=include_manifest]'), | | | 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 | cbLineNumbersWrap: E('#cb-line-numbers'), cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'), previewTarget: E('#fileedit-tab-preview-wrapper'), manifestTarget: E('#fileedit-manifest'), diffTarget: E('#fileedit-tab-diff-wrapper'), cbIsExe: E('input[type=checkbox][name=exec_bit]'), cbManifest: E('input[type=checkbox][name=include_manifest]'), editStatus: E('#fileedit-edit-status'), tabs:{ content: E('#fileedit-tab-content'), preview: E('#fileedit-tab-preview'), diff: E('#fileedit-tab-diff'), commit: E('#fileedit-tab-commit'), fileSelect: E('#fileedit-tab-fileselect') } |
︙ | ︙ | |||
657 658 659 660 661 662 663 | }else if(D.hasClass(P.e.taCommentBig,'hidden')){ P.e.taComment = P.e.taCommentSmall; }else{ P.e.taComment = P.e.taCommentSmall; D.addClass(P.e.taCommentBig, 'hidden'); } D.removeClass(P.e.taComment, 'hidden'); | < > | | 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 | }else if(D.hasClass(P.e.taCommentBig,'hidden')){ P.e.taComment = P.e.taCommentSmall; }else{ P.e.taComment = P.e.taCommentSmall; D.addClass(P.e.taCommentBig, 'hidden'); } D.removeClass(P.e.taComment, 'hidden'); P.tabs.e.container.insertBefore( /* Move the status bar between the tab buttons and tab panels. Seems to be the best fit in terms of functionality and visibility. */ E('#fossil-status-bar'), P.tabs.e.tabs ); P.tabs.e.container.insertBefore(P.e.editStatus, P.tabs.e.tabs); P.tabs.addEventListener( /* Set up auto-refresh of the preview tab... */ 'before-switch-to', function(ev){ if(ev.detail===P.e.tabs.preview){ P.baseHrefForFile(); if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview(); }else if(ev.detail===P.e.tabs.diff){ /* Work around a weird bug where the page gets wider than the window when the diff tab is NOT in view and the current SBS diff widget is wider than the window. When the diff IS in view then CSS overflow magically reduces the page size again. Weird. Maybe FF-specific. Note that this weirdness happens even though P.e.diffTarget's parent |
︙ | ︙ | |||
713 714 715 716 717 718 719 720 721 | diffButtons.querySelector('button.unified').addEventListener( "click",(e)=>P.diff(false), false ); P.e.btnCommit.addEventListener( "click",(e)=>P.commit(), false ); F.confirmer(P.e.btnReload, { confirmText: "Really reload, losing edits?", onconfirm: (e)=>P.unstashContent().loadFile(), | > | | 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 | diffButtons.querySelector('button.unified').addEventListener( "click",(e)=>P.diff(false), false ); P.e.btnCommit.addEventListener( "click",(e)=>P.commit(), false ); F.confirmer(P.e.btnReload, { pinSize: true, confirmText: "Really reload, losing edits?", onconfirm: (e)=>P.unstashContent().loadFile(), ticks: F.config.confirmerButtonTicks }); E('#comment-toggle').addEventListener( "click",(e)=>P.toggleCommentMode(), false ); P.e.taEditor.addEventListener( 'change', ()=>P.stashContentChange(), false |
︙ | ︙ | |||
772 773 774 775 776 777 778 | new Event('change',{target:selectFontSize}) ); } P.addEventListener( // Clear certain views when new content is loaded/set 'fileedit-content-replaced', | > > | > < | 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 | new Event('change',{target:selectFontSize}) ); } P.addEventListener( // Clear certain views when new content is loaded/set 'fileedit-content-replaced', ()=>{ P.previewNeedsUpdate = true; D.clearElement(P.e.diffTarget, P.e.previewTarget, P.e.manifestTarget); } ); P.addEventListener( // Clear certain views after a non-dry-run commit 'fileedit-committed', (e)=>{ if(!e.detail.dryRun){ D.clearElement(P.e.diffTarget, P.e.previewTarget); } } ); P.fileSelectWidget.init(); P.stashWidget.init( P.e.tabs.content.lastElementChild ); }/*F.onPageLoad()*/); /** Getter (if called with no args) or setter (if passed an arg) for the current file content. |
︙ | ︙ | |||
817 818 819 820 821 822 823 | /** For use when installing a custom editor widget. Pass it the getter and setter callbacks to fetch resp. set the content of the custom widget. They will be triggered via P.fileContent(). Returns this object. */ | | | 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 | /** For use when installing a custom editor widget. Pass it the getter and setter callbacks to fetch resp. set the content of the custom widget. They will be triggered via P.fileContent(). Returns this object. */ P.setContentMethods = function(getter, setter){ this.fileContent.get = getter; this.fileContent.set = setter; return this; }; /** Removes the default editor widget (and any dependent elements) |
︙ | ︙ | |||
927 928 929 930 931 932 933 | /** updateVersion() updates the filename and version in various UI elements... Returns this object. */ | | > > > > | | | > | > > > > > | | < < < > > | | < | > | | < < < < < < < < < < | < < | < | < < < < | | 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 | /** updateVersion() updates the filename and version in various UI elements... Returns this object. */ P.updateVersion = function f(file,rev){ if(!f.eLinks){ f.eName = P.e.editStatus.querySelector('span.name'); f.eLinks = P.e.editStatus.querySelector('span.links'); } if(1===arguments.length){/*assume object*/ this.finfo = arguments[0]; file = this.finfo.filename; rev = this.finfo.checkin; }else if(0===arguments.length){ if(affirmHasFile()){ file = this.finfo.filename; rev = this.finfo.checkin; } }else{ this.finfo = {filename:file,checkin:rev}; } const fi = this.finfo; D.clearElement(f.eName, f.eLinks); if(!fi){ D.append(f.eName, '(no file loaded)'); return this; } const rHuman = F.hashDigits(rev), rUrl = F.hashDigits(rev,true); //TODO? port over is-edited marker from /wikiedit //var marker = getEditMarker(wi, false); D.append(f.eName/*,marker*/,D.a(F.repoUrl('finfo',{name:file, m:rUrl}), file)); D.append( f.eLinks, D.append(D.span(), fi.mimetype||'?mimetype?'), D.a(F.repoUrl('info/'+rUrl), rHuman), D.a(F.repoUrl('timeline',{m:rUrl}), "timeline"), D.a(F.repoUrl('annotate',{filename:file, checkin:rUrl}),'annotate'), D.a(F.repoUrl('blame',{filename:file, checkin:rUrl}),'blame') ); const purlArgs = F.encodeUrlArgs({ filename: this.finfo.filename, checkin: rUrl },false,true); const purl = F.repoUrl('fileedit',purlArgs); D.append( f.eLinks, D.a(purl,"editor permalink") ); this.setPageTitle("Edit: "+fi.filename); return this; }; /** loadFile() loads (file,checkinVersion) and updates the relevant UI elements to reflect the loaded state. If passed no arguments then it re-uses the values from the currently-loaded file, reloading |
︙ | ︙ | |||
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 | branch: headers['x-fileedit-checkin-branch'], isExe: ('x'===headers['x-fileedit-file-perm']), mimetype: headers['content-type'].split(';').shift() }); self.tabs.switchToTab(self.e.tabs.content); self.e.cbIsExe.checked = self.finfo.isExe; self.fileContent(r); self.dispatchEvent('fileedit-file-loaded', self.finfo); }; const semiFinfo = {filename: file, checkin: rev}; const stashFinfo = this.getStashedFinfo(semiFinfo); if(stashFinfo){ // fake a response from the stash... this.finfo = stashFinfo; this.e.cbIsExe.checked = !!stashFinfo.isExe; | > | 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 | branch: headers['x-fileedit-checkin-branch'], isExe: ('x'===headers['x-fileedit-file-perm']), mimetype: headers['content-type'].split(';').shift() }); self.tabs.switchToTab(self.e.tabs.content); self.e.cbIsExe.checked = self.finfo.isExe; self.fileContent(r); P.previewNeedsUpdate = true; self.dispatchEvent('fileedit-file-loaded', self.finfo); }; const semiFinfo = {filename: file, checkin: rev}; const stashFinfo = this.getStashedFinfo(semiFinfo); if(stashFinfo){ // fake a response from the stash... this.finfo = stashFinfo; this.e.cbIsExe.checked = !!stashFinfo.isExe; |
︙ | ︙ | |||
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 | responseHeaders: 'x-ajax-render-mode', onload: (r,header)=>{ P.selectPreviewMode(P.previewModes[header]); if('wiki'===header) P.baseHrefForFile(); else P.baseHrefRestore(); callback(r); F.message('Updated preview.'); P.dispatchEvent('fileedit-preview-updated',{ previewMode: P.previewModes.current, mimetype: P.finfo.mimetype, element: P.e.previewTarget }); }, onerror: (e)=>{ | > | 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 | responseHeaders: 'x-ajax-render-mode', onload: (r,header)=>{ P.selectPreviewMode(P.previewModes[header]); if('wiki'===header) P.baseHrefForFile(); else P.baseHrefRestore(); callback(r); F.message('Updated preview.'); P.previewNeedsUpdate = false; P.dispatchEvent('fileedit-preview-updated',{ previewMode: P.previewModes.current, mimetype: P.finfo.mimetype, element: P.e.previewTarget }); }, onerror: (e)=>{ |
︙ | ︙ | |||
1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 | if(onlyFinfo && $stash.hasStashedContent(fi)){ $stash.updateFile(fi); }else{ $stash.updateFile(fi, P.fileContent()); } F.message("Stashed change to",F.hashDigits(fi.checkin),fi.filename); $stash.prune(); } return this; }; /** Removes any stashed state for the current P.finfo (if set) from F.storage. Returns this. */ P.unstashContent = function(){ const finfo = arguments[0] || this.finfo; if(finfo){ $stash.unstash(finfo); //console.debug("Unstashed",finfo); F.message("Unstashed",F.hashDigits(finfo.checkin),finfo.filename); } return this; }; | > > | 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 | if(onlyFinfo && $stash.hasStashedContent(fi)){ $stash.updateFile(fi); }else{ $stash.updateFile(fi, P.fileContent()); } F.message("Stashed change to",F.hashDigits(fi.checkin),fi.filename); $stash.prune(); this.previewNeedsUpdate = true; } return this; }; /** Removes any stashed state for the current P.finfo (if set) from F.storage. Returns this. */ P.unstashContent = function(){ const finfo = arguments[0] || this.finfo; if(finfo){ this.previewNeedsUpdate = true; $stash.unstash(finfo); //console.debug("Unstashed",finfo); F.message("Unstashed",F.hashDigits(finfo.checkin),finfo.filename); } return this; }; |
︙ | ︙ |
Changes to src/fossil.page.forumpost.js.
︙ | ︙ | |||
9 10 11 12 13 14 15 | on contentElem when the given widget is activated. */ const getWidgetHandler = function(widget, contentElem){ return function(ev){ if(ev) ev.preventDefault(); const wasExpanded = widget.classList.contains('expanded'); widget.classList.toggle('expanded'); contentElem.classList.toggle('expanded'); | | > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > | > | | < < < | > | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | on contentElem when the given widget is activated. */ const getWidgetHandler = function(widget, contentElem){ return function(ev){ if(ev) ev.preventDefault(); const wasExpanded = widget.classList.contains('expanded'); widget.classList.toggle('expanded'); contentElem.classList.toggle('expanded'); if(wasExpanded){ contentElem.classList.add('shrunken'); contentElem.parentElement.scrollIntoView({ /* This is non-standard, but !(MSIE, Safari) supposedly support it: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#Browser_compatibility */ behavior: 'smooth' }); }else{ contentElem.classList.remove('shrunken'); } return false; }; }; /* Adds an Expand/Collapse toggle to all div.forumPostBody elements which are deemed "too large" (those for which scrolling is currently activated because they are taller than their max-height). */ document.querySelectorAll( 'div.forumHier, div.forumTime, div.forumHierRoot' ).forEach(function f(forumPostWrapper){ const content = forumPostWrapper.querySelector('div.forumPostBody'); if(!content || !scrollbarIsVisible(content)) return; const parent = content.parentElement, widget = D.addClass( D.div(), 'forum-post-collapser','bottom' ), rightTapZone = D.addClass( D.div(), 'forum-post-collapser','right' ); /* Repopulates the rightTapZone with arrow indicators. Because of the wildly varying height of these elements, This has to be done dynamically at init time and upon collapse/expand. Will not work until the rightTapZone has been added to the DOM. */ const refillTapZone = function f(){ if(!f.baseTapIndicatorHeight){ /* To figure out how often to place an arrow in the rightTapZone, we simply grab the first header element from the page and use its hight as our basis for calculation. */ const h1 = document.querySelector('h1, h2'); f.baseTapIndicatorHeight = h1.getBoundingClientRect().height; } D.clearElement(rightTapZone); var rtzHeight = parseInt(window.getComputedStyle(rightTapZone).height); do { D.append(rightTapZone, D.span()); rtzHeight -= f.baseTapIndicatorHeight * 8; }while(rtzHeight>0); }; const handlerStep1 = getWidgetHandler(widget, content); const widgetEventHandler = ()=>{ handlerStep1(); refillTapZone(); }; content.classList.add('with-expander'); widget.addEventListener('click', widgetEventHandler, false); /** Append 3 children, which CSS will evenly space across the widget. This improves visibility over having the label in only the left, right, or center. */ var i = 0; for( ; i < 3; ++i ) D.append(widget, D.span()); if(content.nextSibling){ forumPostWrapper.insertBefore(widget, content.nextSibling); }else{ forumPostWrapper.appendChild(widget); } content.appendChild(rightTapZone); rightTapZone.addEventListener('click', widgetEventHandler, false); refillTapZone(); }); })/*onload callback*/; })(window.fossil); |
Added src/fossil.page.wikiedit.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 | (function(F/*the fossil object*/){ "use strict"; /** Client-side implementation of the /wikiedit app. Requires that the fossil JS bootstrapping is complete and that several fossil JS APIs have been installed: fossil.fetch, fossil.dom, fossil.tabs, fossil.storage, fossil.confirmer. Custom events which can be listened for via fossil.page.addEventListener(): - Event 'wiki-page-loaded': passes on information when it loads a wiki (whether from the network or its internal local-edit cache), in the form of an "winfo" object: { name: string, mimetype: mimetype string, type: "normal" | "tag" | "checkin" | "branch" | "sandbox", version: UUID string or null for a sandbox page or new page, parent: parent UUID string or null if no parent, isEmpty: true if page has no content (is "deleted"). content: string, optional in most contexts } The internal docs and code frequently use the term "winfo", and such references refer to an object with that form. The fossil.page.wikiContent() method gets or sets the current file content for the page. - Event 'wiki-saved': is fired when a commit completes, passing on the same info as wiki-page-loaded. - Event 'wiki-content-replaced': when the editor's content is replaced, as opposed to it being edited via user interaction. This normally happens via selecting a file to load. The event detail is the fossil.page object, not the current file content. - Event 'wiki-preview-updated': when the preview is refreshed from the server, this event passes on information about the preview change in the form of an object: { element: the DOM element which contains the content preview. mimetype: the page's mimetype. } Here's an example which can be used with the highlightjs code highlighter to update the highlighting when the preview is refreshed in "wiki" mode (which includes fossil-native wiki and markdown): fossil.page.addEventListener( 'wiki-preview-updated', (ev)=>{ if(ev.detail.mimetype!=='text/plain'){ ev.detail.element.querySelectorAll( 'code[class^=language-]' ).forEach((e)=>hljs.highlightBlock(e)); } } ); */ const E = (s)=>document.querySelector(s), D = F.dom, P = F.page; P.config = { /* Max number of locally-edited pages to stash, after which we drop the least-recently used. */ defaultMaxStashSize: 10, useConfirmerButtons:{ /* If true during fossil.page setup, certain buttons will use a "confirmer" step, else they will not. The confirmer topic has been the source of much contention in the forum. */ save: false, reload: true, discardStash: true } }; /** $stash is an internal-use-only object for managing "stashed" local edits, to help avoid that users accidentally lose content by switching tabs or following links or some such. The basic theory of operation is... All "stashed" state is stored using fossil.storage. - When the current wiki content is modified by the user, the current state of the page is stashed. - When saving, the stashed entry for the previous version is removed from the stash. - When "loading", we use any stashed state for the given checkin/file combination. When forcing a re-load of content, any stashed entry for that combination is removed from the stash. - Every time P.stashContentChange() updates the stash, it is pruned to $stash.prune.defaultMaxCount most-recently-updated entries. - This API often refers to "winfo objects." Those are objects with a minimum of {page,mimetype} properties (which must be valid), and the page name is used as basis for the stash keys for any given page. The structure of the stash is a bit convoluted for efficiency's sake: we store a map of file info (winfo) objects separately from those files' contents because otherwise we would be required to JSONize/de-JSONize the file content when stashing/restoring it, and that would be horribly inefficient (meaning "battery-consuming" on mobile devices). */ const $stash = { keys: { index: F.page.name+'.index' }, /** index: { "PAGE_NAME": {wiki page info w/o content} ... } In F.storage we... - Store this.index under the key this.keys.index. - Store each page's content under the key (P.name+'/PAGE_NAME'). These are stored separately from the index entries to avoid having to JSONize/de-JSONize the content. The assumption/hope is that the browser can store those records "directly," without any intermediary encoding/decoding going on. */ indexKey: function(winfo){return winfo.name}, /** Returns the key for storing content for the given key suffix, by prepending P.name to suffix. */ contentKey: function(suffix){return P.name+'/'+suffix}, /** Returns the index object, fetching it from the stash or creating it anew on the first call. */ getIndex: function(){ if(!this.index){ this.index = F.storage.getJSON( this.keys.index, {} ); } return this.index; }, _fireStashEvent: function(){ if(this._disableNextEvent) delete this._disableNextEvent; else F.page.dispatchEvent('wiki-stash-updated', this); }, /** Returns the stashed version, if any, for the given winfo object. */ getWinfo: function(winfo){ const ndx = this.getIndex(); return ndx[this.indexKey(winfo)]; }, /** Serializes this object's index to F.storage. Returns this. */ storeIndex: function(){ if(this.index) F.storage.setJSON(this.keys.index,this.index); return this; }, /** Updates the stash record for the given winfo and (optionally) content. If passed 1 arg, only the winfo stash is updated, else both the winfo and its contents are (re-)stashed. Returns this. */ updateWinfo: function(winfo,content){ const ndx = this.getIndex(), key = this.indexKey(winfo), old = ndx[key]; const record = old || (ndx[key]={ name: winfo.name }); record.mimetype = winfo.mimetype; record.type = winfo.type; record.parent = winfo.parent; record.version = winfo.version; record.stashTime = new Date().getTime(); record.isEmpty = !!winfo.isEmpty; this.storeIndex(); if(arguments.length>1){ if(content) delete record.isEmpty; F.storage.set(this.contentKey(key), content); } this._fireStashEvent(); return this; }, /** Returns the stashed content, if any, for the given winfo object. */ stashedContent: function(winfo){ return F.storage.get(this.contentKey(this.indexKey(winfo))); }, /** Returns true if we have stashed content for the given winfo record or page name. */ hasStashedContent: function(winfo){ if('string'===typeof winfo) winfo = {name: winfo}; return F.storage.contains(this.contentKey(this.indexKey(winfo))); }, /** Unstashes the given winfo record and its content. Returns this. */ unstash: function(winfo){ const ndx = this.getIndex(), key = this.indexKey(winfo); delete winfo.stashTime; delete ndx[key]; F.storage.remove(this.contentKey(key)); this.storeIndex(); this._fireStashEvent(); return this; }, /** Clears all $stash entries from F.storage. Returns this. */ clear: function(){ const ndx = this.getIndex(), self = this; let count = 0; Object.keys(ndx).forEach(function(k){ ++count; const e = ndx[k]; delete ndx[k]; F.storage.remove(self.contentKey(k)); }); F.storage.remove(this.keys.index); delete this.index; if(count) this._fireStashEvent(); return this; }, /** Removes all but the maxCount most-recently-updated stash entries, where maxCount defaults to this.prune.defaultMaxCount. */ prune: function f(maxCount){ const ndx = this.getIndex(); const li = []; if(!maxCount || maxCount<0) maxCount = f.defaultMaxCount; Object.keys(ndx).forEach((k)=>li.push(ndx[k])); li.sort((l,r)=>l.stashTime - r.stashTime); let n = 0; while(li.length>maxCount){ ++n; const e = li.shift(); this._disableNextEvent = true; this.unstash(e); console.warn("Pruned oldest local file edit entry:",e); } if(n) this._fireStashEvent(); } }; $stash.prune.defaultMaxCount = P.config.defaultMaxStashSize || 10; P.$stash = $stash /* we have to expose this for the new-page case :/ */; /** Internal workaround to select the current preview mode and fire a change event if the value actually changes or if forceEvent is truthy. */ P.selectMimetype = function(modeValue, forceEvent){ const s = this.e.selectMimetype; if(!modeValue) modeValue = s.value; else if(s.value != modeValue){ s.value = modeValue; forceEvent = true; } if(forceEvent){ // Force UI update s.dispatchEvent(new Event('change',{target:s})); } }; /** Internal helper to get an edit status indicator for the given winfo object. Pass it a winfo object or one of the "constants" which are assigned as member properties of this function (see below its definition). */ const getEditMarker = function f(winfo, textOnly){ const esm = F.config.editStateMarkers; if(f.NEW===winfo){ /* force is-new */ return textOnly ? esm.isNew : D.addClass(D.append(D.span(),esm.isNew), 'is-new'); }else if(f.MODIFIED===winfo){ /* force is-modified */ return textOnly ? esm.isModified : D.addClass(D.append(D.span(),esm.isModified), 'is-modified'); }else if(f.DELETED===winfo){/* force is-deleted */ return textOnly ? esm.isDeleted : D.addClass(D.append(D.span(),esm.isDeleted), 'is-deleted'); }else if(winfo && winfo.version){ /* is existing page modified? */ if($stash.getWinfo(winfo)){ return textOnly ? esm.isModified : D.addClass(D.append(D.span(),esm.isModified), 'is-modified'); } /*fall through*/ } else if(winfo){ /* is new non-sandbox or is modified sandbox? */ if('sandbox'!==winfo.type){ return textOnly ? esm.isNew : D.addClass(D.append(D.span(),esm.isNew), 'is-new'); }else if($stash.getWinfo(winfo)){ return textOnly ? esm.isModified : D.addClass(D.append(D.span(),esm.isModified), 'is-modified'); } } return textOnly ? '' : D.span(); }; getEditMarker.NEW = 1; getEditMarker.MODIFIED = 2; getEditMarker.DELETED = 3; /** Returns undefined if winfo is falsy, true if the given winfo object appears to be "new", else returns false. */ const winfoIsNew = function(winfo){ if(!winfo) return undefined; else if('sandbox' === winfo.type) return false; else return !winfo.version; }; /** Sets up and maintains the widgets for the list of wiki pages. */ const WikiList = { e: { filterCheckboxes: { /*map of wiki page type to checkbox for list filtering purposes, except for "sandbox" type, which is assumed to be covered by the "normal" type filter. */}, }, cache: { pageList: [], optByName:{/*map of page names to OPTION object, to speed up certain operations.*/}, names: { /* Map of page names to "something." We don't map to their winfo bits because those regularly get swapped out via de/serialization. We need this map to support the add-new-page feature, to give us a way to check for dupes without asking the server or walking through the whole selection list. */} }, /** Updates OPTION elements to reflect whether the page has local changes or is new/unsaved. This implementation is horribly inefficient, in that we have to walk and validate the whole list for each stash-level change. If passed an argument, it is assumed to be an OPTION element and only that element is updated, else all OPTION elements in this.e.select are updated. Reminder to self: in order to mark is-edited/is-new state we have to update the OPTION element's inner text to reflect the is-modified/is-new flags, rather than use CSS classes to tag them, because mobile Chrome can neither restyle OPTION elements no render ::before content on them. We *also* use CSS tags, but they aren't sufficient for the mobile browsers. */ _refreshStashMarks: function callee(option){ if(!callee.eachOpt){ const self = this; callee.eachOpt = function(keyOrOpt){ const opt = 'string'===typeof keyOrOpt ? self.e.select.options[keyOrOpt] : keyOrOpt; const stashed = $stash.getWinfo({name:opt.value}); var prefix = ''; D.removeClass(opt, 'stashed', 'stashed-new', 'deleted'); if(stashed){ const isNew = winfoIsNew(stashed); prefix = getEditMarker(isNew ? getEditMarker.NEW : getEditMarker.MODIFIED, true); D.addClass(opt, isNew ? 'stashed-new' : 'stashed'); D.removeClass(opt, 'deleted'); }else if(opt.dataset.isDeleted){ prefix = getEditMarker(getEditMarker.DELETED,true); D.addClass(opt, 'deleted'); } opt.innerText = prefix + opt.value; self.cache.names[opt.value] = true; }; } if(arguments.length){ callee.eachOpt(option); }else{ this.cache.names = {/*must reset it to acount for local page removals*/}; Object.keys(this.e.select.options).forEach(callee.eachOpt); } }, /** Removes the given wiki page entry from the page selection list, if it's in the list. */ removeEntry: function(name){ const sel = this.e.select; var ndx = sel.selectedIndex; sel.value = name; if(sel.selectedIndex>-1){ if(ndx === sel.selectedIndex) ndx = -1; sel.options.remove(sel.selectedIndex); } sel.selectedIndex = ndx; delete this.cache.names[name]; delete this.cache.optByName[name]; this.cache.pageList = this.cache.pageList.filter((wi)=>name !== wi.name); }, /** Rebuilds the selection list. Necessary when it's loaded from the server, we locally create a new page, or we remove a locally-created new page. */ _rebuildList: function callee(){ /* Jump through some hoops to integrate new/unsaved pages into the list of existing pages... We use a map as an intermediary in order to filter out any local-stash dupes from server-side copies. */ const list = this.cache.pageList; if(!list) return; if(!callee.sorticase){ callee.sorticase = function(l,r){ if(l===r) return 0; l = l.toLowerCase(); r = r.toLowerCase(); return l<=r ? -1 : 1; }; } const map = {}, ndx = $stash.getIndex(), sel = this.e.select; D.clearElement(sel); list.forEach((winfo)=>map[winfo.name] = winfo); Object.keys(ndx).forEach(function(key){ const winfo = ndx[key]; if(!winfo.version/*new page*/) map[winfo.name] = winfo; }); const self = this; Object.keys(map) .sort(callee.sorticase) .forEach(function(name){ const winfo = map[name]; const opt = D.option(sel, winfo.name); const wtype = opt.dataset.wtype = winfo.type==='sandbox' ? 'normal' : (winfo.type||'normal'); const cb = self.e.filterCheckboxes[wtype]; self.cache.optByName[winfo.name] = opt; if(cb && !cb.checked) D.addClass(opt, 'hidden'); if(winfo.isEmpty){ opt.dataset.isDeleted = true; } self._refreshStashMarks(opt); }); D.enable(sel); if(P.winfo) sel.value = P.winfo.name; }, /** Loads the page list and populates the selection list. */ loadList: function callee(){ if(!callee.onload){ const self = this; callee.onload = function(list){ self.cache.pageList = list; self._rebuildList(); F.message("Loaded page list."); }; } if(P.initialPageList){ /* ^^^ injected at page-creation time. */ const list = P.initialPageList; delete P.initialPageList; callee.onload(list); }else{ F.fetch('wikiajax/list',{ urlParams:{verbose:true}, responseType: 'json', onload: callee.onload }); } return this; }, /** Returns true if the given name appears to be a valid wiki page name, noting that the final arbitrator is the server. On validation error it emits a message via fossil.error() and returns false. */ validatePageName: function(name){ var err; if(!name){ err = "may not be empty"; }else if(this.cache.names.hasOwnProperty(name)){ err = "page already exists: "+name; }else if(name.length>100){ err = "too long (limit is 100)"; }else if(/\s{2,}/.test(name)){ err = "multiple consecutive spaces"; }else if(/[\t\r\n]/.test(name)){ err = "contains control character(s)"; }else{ let i = 0, n = name.length, c; for( ; i < n; ++i ){ if(name.charCodeAt(i)<0x20){ err = "contains control character(s)"; break; } } } if(err){ F.error("Invalid name:",err); } return !err; }, /** If the given name is valid, a new page with that (trimmed) name is added to the local stash. */ addNewPage: function(name){ name = name.trim(); if(!this.validatePageName(name)) return false; var wtype = 'normal'; if(0===name.indexOf('checkin/')) wtype = 'checkin'; else if(0===name.indexOf('branch/')) wtype = 'branch'; else if(0===name.indexOf('tag/')) wtype = 'tag'; /* ^^^ note that we're not validating that, e.g., checkin/XYZ has a full artifact ID after "checkin/". */ const winfo = { name: name, type: wtype, mimetype: 'text/x-fossil-wiki', version: null, parent: null }; this.cache.pageList.push( winfo/*keeps entry from getting lost from the list on save*/ ); $stash.updateWinfo(winfo, ''); this._rebuildList(); P.loadPage(winfo.name); return true; }, /** Installs a wiki page selection list into the given parent DOM element and loads the page list from the server. */ init: function(parentElem){ const sel = D.select(), btn = D.addClass(D.button("Reload page list"), 'save'); this.e.select = sel; D.addClass(parentElem, 'WikiList'); D.clearElement(parentElem); D.append( parentElem, D.append(D.fieldset("Select a page to edit"), sel) ); D.attr(sel, 'size', 12); D.option(D.disable(D.clearElement(sel)), "Loading..."); /** Set up filter checkboxes for the various types of wiki pages... */ const fsFilter = D.fieldset("Page types"), fsFilterBody = D.div(), filters = ['normal', 'branch/...', 'tag/...', 'checkin/...'] ; D.append(fsFilter, fsFilterBody); D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch'); // Add filters by page type... const self = this; const filterByType = function(wtype, show){ sel.querySelectorAll('option[data-wtype='+wtype+']').forEach(function(opt){ if(show) opt.classList.remove('hidden'); else opt.classList.add('hidden'); }); }; filters.forEach(function(label){ const wtype = label.split('/')[0]; const cbId = 'wtype-filter-'+wtype, lbl = D.attr(D.append(D.label(),label), 'for', cbId), cb = D.attr(D.input('checkbox'), 'id', cbId); D.append(fsFilterBody, D.append(D.span(), cb, lbl)); self.e.filterCheckboxes[wtype] = cb; cb.checked = true; filterByType(wtype, cb.checked); cb.addEventListener( 'change', function(ev){filterByType(wtype, ev.target.checked)}, false ); }); { /* add filter for "deleted" pages */ const cbId = 'wtype-filter-deleted', lbl = D.attr(D.append(D.label(), getEditMarker(getEditMarker.DELETED,false), 'deleted'), 'for', cbId), cb = D.attr(D.input('checkbox'), 'id', cbId); cb.checked = false; D.addClass(parentElem,'hide-deleted'); D.attr(lbl, 'title', 'Fossil considers empty pages to be "deleted" in some contexts.'); D.append(fsFilterBody, D.append(D.span(), cb, lbl)); cb.addEventListener( 'change', function(ev){ if(ev.target.checked) D.removeClass(parentElem,'hide-deleted'); else D.addClass(parentElem,'hide-deleted'); }, false); } /* A legend of the meanings of the symbols we use in the OPTION elements to denote certain state. */ const fsLegend = D.fieldset("Edit status"), fsLegendBody = D.div(); D.append(fsLegend, fsLegendBody); D.addClass(fsLegendBody, 'flex-container', 'flex-column', 'stretch'); D.append( fsLegendBody, D.append(D.span(), getEditMarker(getEditMarker.NEW,false)," = new/unsaved"), D.append(D.span(), getEditMarker(getEditMarker.MODIFIED,false)," = has local edits"), D.append(D.span(), getEditMarker(getEditMarker.DELETED,false)," = is empty (deleted)") ); const fsNewPage = D.fieldset("Create new page"), fsNewPageBody = D.div(), newPageName = D.input('text'), newPageBtn = D.button("Add page locally") ; D.append(parentElem, fsNewPage); D.append(fsNewPage, fsNewPageBody); D.addClass(fsNewPageBody, 'flex-container', 'flex-column', 'new-page'); D.append( fsNewPageBody, newPageName, newPageBtn, D.append(D.addClass(D.span(), 'mini-tip'), "New pages exist only in this browser until they are saved.") ); newPageBtn.addEventListener('click', function(){ if(self.addNewPage(newPageName.value)){ newPageName.value = ''; } }, false); D.append( parentElem, D.append(D.addClass(D.div(), 'fieldset-wrapper'), fsFilter, fsNewPage, fsLegend) ); D.append(parentElem, btn); btn.addEventListener('click', ()=>this.loadList(), false); this.loadList(); const onSelect = (e)=>P.loadPage(e.target.value); sel.addEventListener('change', onSelect, false); sel.addEventListener('dblclick', onSelect, false); F.page.addEventListener('wiki-stash-updated', ()=>{ if(P.winfo) this._refreshStashMarks(); else this._rebuildList(); }); F.page.addEventListener('wiki-page-loaded', function(ev){ /* Needed to handle the saved-an-empty-page case. */ const page = ev.detail, opt = self.cache.optByName[page.name]; if(opt){ if(page.isEmpty) opt.dataset.isDeleted = true; else delete opt.dataset.isDeleted; self._refreshStashMarks(opt); }else{ F.error("BUG: internal mis-handling of page object: missing OPTION for page "+page.name); } }); delete this.init; } }; /** Widget for listing and selecting $stash entries. */ P.stashWidget = { e:{/*DOM element(s)*/}, init: function(domInsertPoint/*insert widget BEFORE this element*/){ const wrapper = D.addClass( D.attr(D.div(),'id','wikiedit-stash-selector'), 'input-with-label' ); const sel = this.e.select = D.select(); const btnClear = this.e.btnClear = D.button("Discard Edits"); D.append(wrapper, "Local edits (", D.append(D.code(), F.storage.storageImplName()), "):", sel, btnClear); D.attr(wrapper, "title", [ 'Locally-edited wiki pages. Timestamps are the last local edit time.', 'Only the',P.config.defaultMaxStashSize,'most recent pages', 'are retained. Saving or reloading a file removes it from this list.' ].join(' ')); D.option(D.disable(sel), "(empty)"); P.addEventListener('wiki-stash-updated',(e)=>this.updateList(e.detail)); P.addEventListener('wiki-page-loaded',(e)=>this.updateList($stash, e.detail)); sel.addEventListener('change',function(e){ const opt = this.selectedOptions[0]; if(opt && opt._winfo) P.loadPage(opt._winfo); }); if(F.storage.isTransient()){/*Warn if our storage is particularly transient...*/ D.append(wrapper, D.append( D.addClass(D.span(),'warning'), "Warning: persistent storage is not available, "+ "so uncomitted edits will not survive a page reload." )); } domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint); if(P.config.useConfirmerButtons.discardStash){ /* Must come after btnClear is in the DOM AND the button must not be hidden, else pinned sizing won't work. */ F.confirmer(btnClear, { pinSize: true, confirmText: "DISCARD all local edits?", onconfirm: ()=>P.clearStash(), ticks: F.config.confirmerButtonTicks }); }else{ btnClear.addEventListener('click', ()=>P.clearStash(), false); } D.addClass(btnClear,'hidden'); $stash._fireStashEvent(/*read the page-load-time stash*/); delete this.init; }, /** Regenerates the edit selection list. */ updateList: function f(stasher,theWinfo){ if(!f.compare){ const cmpBase = (l,r)=>l<r ? -1 : (l===r ? 0 : 1); f.compare = (l,r)=>cmpBase(l.name.toLowerCase(), r.name.toLowerCase()); f.rxZ = /\.\d+Z$/ /* ms and 'Z' part of date string */; const pad=(x)=>(''+x).length>1 ? x : '0'+x; f.timestring = function(d){ return [ d.getFullYear(),'-',pad(d.getMonth()+1/*sigh*/),'-',pad(d.getDate()), '@',pad(d.getHours()),':',pad(d.getMinutes()) ].join(''); }; } const index = stasher.getIndex(), ilist = []; Object.keys(index).forEach((winfo)=>{ ilist.push(index[winfo]); }); const self = this; D.clearElement(this.e.select); if(0===ilist.length){ D.addClass(this.e.btnClear, 'hidden'); D.option(D.disable(this.e.select),"No local edits"); return; } D.enable(this.e.select); if(true){ /* The problem with this Clear button is that it allows the user to nuke a non-empty newly-added page without the failsafe confirmation we have if they use P.e.btnReload. Not yet sure how best to resolve that. */ D.removeClass(this.e.btnClear, 'hidden'); } D.disable(D.option(this.e.select,0,"Select a local edit...")); const currentWinfo = theWinfo || P.winfo || {name:''}; ilist.sort(f.compare).forEach(function(winfo,n){ const key = stasher.indexKey(winfo), rev = winfo.version || ''; const opt = D.option( self.e.select, n+1/*value is (almost) irrelevant*/, [winfo.name, ' [', rev ? F.hashDigits(rev) : ( winfo.type==='sandbox' ? 'sandbox' : 'new/local' ),'] ', f.timestring(new Date(winfo.stashTime)) ].join('') ); opt._winfo = winfo; if(0===f.compare(currentWinfo, winfo)){ D.attr(opt, 'selected', true); } }); } }/*P.stashWidget*/; /** Keep track of how many in-flight AJAX requests there are so we can disable input elements while any are pending. For simplicity's sake we simply disable ALL OF IT while any AJAX is pending, rather than disabling operation-specific UI elements, which would be a huge maintenance hassle. Noting, however, that this global on/off is not *quite* pedantically correct. Pedantically speaking. If an element is disabled before an XHR starts, this code "should" notice that and not include it in the to-re-enable list. That would be annoying to do, and becomes impossible to do properly once multiple XHRs are in transit and an element is disabled seprately between two of those in-transit requests (that would be an unlikely, but possible, corner case). */ const ajaxState = { count: 0 /* in-flight F.fetch() requests */, toDisable: undefined /* elements to disable during ajax activity */ }; F.fetch.beforesend = function f(){ if(!ajaxState.toDisable){ ajaxState.toDisable = document.querySelectorAll( ['button:not([disabled])', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])' ].join(',') ); } if(1===++ajaxState.count){ D.addClass(document.body, 'waiting'); D.disable(ajaxState.toDisable); } }; F.fetch.aftersend = function(){ if(0===--ajaxState.count){ D.removeClass(document.body, 'waiting'); D.enable(ajaxState.toDisable); delete ajaxState.toDisable /* required to avoid enable/disable race condition with the save button */; } }; F.onPageLoad(function() { document.body.classList.add('wikiedit'); P.base = {tag: E('base'), wikiUrl: F.repoUrl('wiki')}; P.base.originalHref = P.base.tag.href; P.e = { /* various DOM elements we work with... */ taEditor: E('#wikiedit-content-editor'), btnReload: E("#wikiedit-tab-content button.wikiedit-content-reload"), btnSave: E("button.wikiedit-save"), btnSaveClose: D.attr(E("button.wikiedit-save-close"), 'title', 'Save changes and return to the wiki reader.'), selectMimetype: E('select[name=mimetype]'), selectFontSizeWrap: E('#select-font-size'), // selectDiffWS: E('select[name=diff_ws]'), cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'), previewTarget: E('#wikiedit-tab-preview-wrapper'), diffTarget: E('#wikiedit-tab-diff-wrapper'), editStatus: E('#wikiedit-edit-status'), tabContainer: E('#wikiedit-tabs'), tabs:{ pageList: E('#wikiedit-tab-pages'), content: E('#wikiedit-tab-content'), preview: E('#wikiedit-tab-preview'), diff: E('#wikiedit-tab-diff'), misc: E('#wikiedit-tab-misc') //commit: E('#wikiedit-tab-commit') } }; P.tabs = new fossil.TabManager(D.clearElement(P.e.tabContainer)); P.tabs.e.container.insertBefore( /* Move the status bar between the tab buttons and tab panels. Seems to be the best fit in terms of functionality and visibility. */ E('#fossil-status-bar'), P.tabs.e.tabs ); P.tabs.e.container.insertBefore(P.e.editStatus, P.tabs.e.tabs); P.tabs.addEventListener( /* Set up some before-switch-to tab event tasks... */ 'before-switch-to', function(ev){ const theTab = ev.detail, btnSlot = theTab.querySelector('.save-button-slot'); if(btnSlot){ /* Several places make sense for a save button, so we'll move that button around to those tabs where it makes sense. */ btnSlot.parentNode.insertBefore( P.e.btnSave, btnSlot ); btnSlot.parentNode.insertBefore( P.e.btnSaveClose, btnSlot ); P.updateSaveButton(); } if(theTab===P.e.tabs.preview){ P.baseHrefForWiki(); if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview(); }else if(theTab===P.e.tabs.diff){ /* Work around a weird bug where the page gets wider than the window when the diff tab is NOT in view and the current SBS diff widget is wider than the window. When the diff IS in view then CSS overflow magically reduces the page size again. Weird. Maybe FF-specific. Note that this weirdness happens even though P.e.diffTarget's parent is hidden (and therefore P.e.diffTarget is also hidden). */ D.removeClass(P.e.diffTarget, 'hidden'); } } ); P.tabs.addEventListener( /* Set up auto-refresh of the preview tab... */ 'before-switch-from', function(ev){ const theTab = ev.detail; if(theTab===P.e.tabs.preview){ P.baseHrefRestore(); }else if(theTab===P.e.tabs.diff){ /* See notes in the before-switch-to handler. */ D.addClass(P.e.diffTarget, 'hidden'); } } ); F.connectPagePreviewers( P.e.tabs.preview.querySelector( '#btn-preview-refresh' ) ); const diffButtons = E('#wikiedit-tab-diff-buttons'); diffButtons.querySelector('button.sbs').addEventListener( "click",(e)=>P.diff(true), false ); diffButtons.querySelector('button.unified').addEventListener( "click",(e)=>P.diff(false), false ); if(0) P.e.btnCommit.addEventListener( "click",(e)=>P.commit(), false ); const doSave = function(alsoClose){ const w = P.winfo; if(!w){ F.error("No page loaded."); return; } if(alsoClose){ P.save(()=>window.location.href=F.repoUrl('wiki',{name: w.name})); }else{ P.save(); } }; const doReload = function(e){ const w = P.winfo; if(!w){ F.error("No page loaded."); return; } if(!w.version/* new/unsaved page */ && w.type!=='sandbox' && P.wikiContent()){ F.error("This new/unsaved page has content.", "To really discard this page,", "first clear its content", "then use the Discard button."); return; } P.unstashContent(); if(w.version || w.type==='sandbox'){ P.loadPage(w); }else{ WikiList.removeEntry(w.name); delete P.winfo; P.updatePageTitle(); F.message("Discarded new page ["+w.name+"]."); } }; if(P.config.useConfirmerButtons.reload){ F.confirmer(P.e.btnReload, { pinSize: true, confirmText: "Really reload, losing edits?", onconfirm: doReload, ticks: F.config.confirmerButtonTicks }); }else{ P.e.btnReload.addEventListener('click', doReload, false); } if(P.config.useConfirmerButtons.save){ F.confirmer(P.e.btnSave, { pinSize: true, confirmText: "Really save changes?", onconfirm: ()=>doSave(), ticks: F.config.confirmerButtonTicks }); F.confirmer(P.e.btnSaveClose, { pinSize: true, confirmText: "Really save changes?", onconfirm: ()=>doSave(true), ticks: F.config.confirmerButtonTicks }); }else{ P.e.btnSave.addEventListener('click', ()=>doSave(), false); P.e.btnSaveClose.addEventListener('click', ()=>doSave(true), false); } P.e.taEditor.addEventListener( 'change', function(){ P._isDirty = true; P.stashContentChange(); }, false ); P.selectMimetype(false, true); P.e.selectMimetype.addEventListener( 'change', function(e){ if(P.winfo && P.winfo.mimetype !== e.target.value){ P.winfo.mimetype = e.target.value; P._isDirty = true; P.stashContentChange(true); } }, false ); const selectFontSize = E('select[name=editor_font_size]'); if(selectFontSize){ selectFontSize.addEventListener( "change",function(e){ const ed = P.e.taEditor; ed.className = ed.className.replace( /\bfont-size-\d+/g, '' ); ed.classList.add('font-size-'+e.target.value); }, false ); selectFontSize.dispatchEvent( // Force UI update new Event('change',{target:selectFontSize}) ); } P.addEventListener( // Clear certain views when new content is loaded/set 'wiki-content-replaced', ()=>{ P.previewNeedsUpdate = true; D.clearElement(P.e.diffTarget, P.e.previewTarget); } ); P.addEventListener( // Clear certain views after a save 'wiki-saved', (e)=>{ D.clearElement(P.e.diffTarget, P.e.previewTarget); // TODO: replace preview with new content } ); P.addEventListener('wiki-stash-updated',function(){ /* MUST come before WikiList.init() and P.stashWidget.init() so that interwoven event handlers get called in the right order. */ if(P.winfo && !P.winfo.version && !$stash.getWinfo(P.winfo)){ // New local page was removed. delete P.winfo; P.wikiContent(''); P.updatePageTitle(); } P.updateSaveButton(); }).updatePageTitle().updateSaveButton(); P.addEventListener( // Update various state on wiki page load 'wiki-page-loaded', function(ev){ delete P._isDirty; const winfo = ev.detail; P.winfo = winfo; P.previewNeedsUpdate = true; P.e.selectMimetype.value = winfo.mimetype; P.tabs.switchToTab(P.e.tabs.content); P.wikiContent(winfo.content || ''); WikiList.e.select.value = winfo.name; if(!winfo.version && winfo.type!=='sandbox'){ F.message('You are editing a new, unsaved page:',winfo.name); } P.updatePageTitle().updateSaveButton(/* b/c save() routes through here */); }, false ); /* These init()s need to come after P's event handlers are registered */ WikiList.init( P.e.tabs.pageList.firstElementChild ); P.stashWidget.init(P.e.tabs.content.lastElementChild); //P.$wikiList = WikiList/*only for testing/debugging*/; }/*F.onPageLoad()*/); /** Returns true if fossil.page.winfo is set, indicating that a page has been loaded, else it reports an error and returns false. If passed a truthy value any error message about not having a wiki page loaded is suppressed. */ const affirmPageLoaded = function(quiet){ if(!P.winfo && !quiet) F.error("No wiki page is loaded."); return !!P.winfo; }; /** Updates the in-tab title/edit status information */ P.updateEditStatus = function f(){ if(!f.eLinks){ f.eName = P.e.editStatus.querySelector('span.name'); f.eLinks = P.e.editStatus.querySelector('span.links'); } const wi = this.winfo; D.clearElement(f.eName, f.eLinks); if(!wi){ D.append(f.eName, '(no page loaded)'); return; } var marker = getEditMarker(wi, false); D.append(f.eName,marker,wi.name); if(wi.version){ D.append( f.eLinks, D.a(F.repoUrl('wiki',{name:wi.name}),"viewer"), D.a(F.repoUrl('whistory',{name:wi.name}),'history'), D.a(F.repoUrl('attachlist',{page:wi.name}),"attachments"), D.a(F.repoUrl('attachadd',{page:wi.name,from: F.repoUrl('wikiedit',{name: wi.name})}), "attach"), D.a(F.repoUrl('wikiedit',{name:wi.name}),"editor permalink") ); } }; /** Update the page title and header based on the state of this.winfo. A no-op if this.winfo is not set. Returns this. */ P.updatePageTitle = function f(){ if(!f.titleElement){ f.titleElement = document.head.querySelector('title'); } const wi = P.winfo, marker = getEditMarker(wi, true), title = wi ? wi.name : 'no page loaded'; f.titleElement.innerText = 'Wiki Editor: ' + marker + title; this.updateEditStatus(); return this; }; /** Change the save button depending on whether we have stuff to save or not. */ P.updateSaveButton = function(){ /** // Currently disabled, per forum feedback and platform-level // event-handling compatibility, but might be revisited. We now // use an is-dirty flag instead to prevent saving when no change // event has fired for the current doc. if(!this.winfo || !this.getStashedWinfo(this.winfo)){ D.disable(this.e.btnSave).innerText = "No changes to save"; D.disable(this.e.btnSaveClose); }else{ D.enable(this.e.btnSave).innerText = "Save"; D.enable(this.e.btnSaveClose); }*/ return this; }; /** Getter (if called with no args) or setter (if passed an arg) for the current file content. The setter form sets the content, dispatches a 'wiki-content-replaced' event, and returns this object. */ P.wikiContent = function f(){ if(0===arguments.length){ return f.get(); }else{ f.set(arguments[0] || ''); this.dispatchEvent('wiki-content-replaced', this); return this; } }; /* Default get/set impls for file content */ P.wikiContent.get = function(){return P.e.taEditor.value}; P.wikiContent.set = function(content){P.e.taEditor.value = content}; /** For use when installing a custom editor widget. Pass it the getter and setter callbacks to fetch resp. set the content of the custom widget. They will be triggered via P.wikiContent(). Returns this object. */ P.setContentMethods = function(getter, setter){ this.wikiContent.get = getter; this.wikiContent.set = setter; return this; }; /** Removes the default editor widget (and any dependent elements) from the DOM, adds the given element in its place, removes this method from this object, and returns this object. */ P.replaceEditorElement = function(newEditor){ P.e.taEditor.parentNode.insertBefore(newEditor, P.e.taEditor); P.e.taEditor.remove(); P.e.selectFontSizeWrap.remove(); delete this.replaceEditorElement; return P; }; /** Sets the current page's base.href to {g.zTop}/wiki. */ P.baseHrefForWiki = function f(){ this.base.tag.href = this.base.wikiUrl; return this; }; /** Sets the document's base.href value to its page-load-time setting. */ P.baseHrefRestore = function(){ this.base.tag.href = this.base.originalHref; }; /** loadPage() loads the given wiki page and updates the relevant UI elements to reflect the loaded state. If passed no arguments then it re-uses the values from the currently-loaded page, reloading it (emitting an error message if no file is loaded). Returns this object, noting that the load is async. After loading it triggers a 'wiki-page-loaded' event, passing it this.winfo. If a locally-edited copy of the given file/rev is found, that copy is used instead of one fetched from the server, but it is still treated as a load event. Alternate call forms: - no arguments: re-loads from this.winfo. - 1 non-string argument: assumed to be an winfo-style object. Must have at least the {name} property, but need not have other winfo state. */ P.loadPage = function(name){ if(0===arguments.length){ /* Reload from this.winfo */ if(!affirmPageLoaded()) return this; name = this.winfo.name; }else if(1===arguments.length && 'string' !== typeof name){ /* Assume winfo-like object */ const arg = arguments[0]; name = arg.name; } const onload = (r)=>{ this.dispatchEvent('wiki-page-loaded', r); }; const stashWinfo = this.getStashedWinfo({name: name}); if(stashWinfo){ // fake a response from the stash... F.message("Fetched from the local-edit storage:", stashWinfo.name); onload({ name: stashWinfo.name, mimetype: stashWinfo.mimetype, type: stashWinfo.type, version: stashWinfo.version, parent: stashWinfo.parent, isEmpty: !!stashWinfo.isEmpty, content: $stash.stashedContent(stashWinfo) }); this._isDirty = true/*b/c loading normally clears that flag*/; return this; } F.message( "Loading content..." ).fetch('wikiajax/fetch',{ urlParams: { page: name }, responseType: 'json', onload:(r)=>{ F.message('Loaded page ['+r.name+'].'); onload(r); } }); return this; }; /** Fetches the page preview based on the contents and settings of this page's input fields, and updates the UI with with the preview. Returns this object, noting that the operation is async. */ P.preview = function f(switchToTab){ if(!affirmPageLoaded()) return this; const target = this.e.previewTarget, self = this; const updateView = function(c){ D.clearElement(target); if('string'===typeof c) target.innerHTML = c; if(switchToTab) self.tabs.switchToTab(self.e.tabs.preview); }; return this._postPreview(this.wikiContent(), updateView); }; /** Callback for use with F.connectPagePreviewers() */ P._postPreview = function(content,callback){ if(!affirmPageLoaded()) return this; if(!content){ callback(content); return this; } const fd = new FormData(); const mimetype = this.e.selectMimetype.value; fd.append('page', this.winfo.name); fd.append('mimetype',mimetype); fd.append('content',content || ''); F.message( "Fetching preview..." ).fetch('wikiajax/preview',{ payload: fd, onload: (r,header)=>{ callback(r); F.message('Updated preview.'); P.previewNeedsUpdate = false; P.dispatchEvent('wiki-preview-updated',{ mimetype: mimetype, element: P.e.previewTarget }); }, onerror: (e)=>{ fossil.fetch.onerror(e); callback("Error fetching preview: "+e); } }); return this; }; /** Undo some of the SBS diff-rendering bits which hurt us more than they help... */ P.tweakSbsDiffs2 = function(){ if(1){ const dt = this.e.diffTarget; dt.querySelectorAll('.sbsdiffcols .difftxtcol').forEach( (dtc)=>{ const pre = dtc.querySelector('pre'); pre.style.width = 'initial'; //pre.removeAttribute('style'); //console.debug("pre width =",pre.style.width); } ); } this.tweakSbsDiffs(); }; /** Fetches the content diff based on the contents and settings of this page's input fields, and updates the UI with the diff view. Returns this object, noting that the operation is async. */ P.diff = function f(sbs){ if(!affirmPageLoaded()) return this; const content = this.wikiContent(), self = this, target = this.e.diffTarget; const fd = new FormData(); fd.append('page',this.winfo.name); fd.append('sbs', sbs ? 1 : 0); fd.append('content',content); if(this.e.selectDiffWS) fd.append('ws',this.e.selectDiffWS.value); F.message( "Fetching diff..." ).fetch('wikiajax/diff',{ payload: fd, onload: function(c){ target.innerHTML = [ "<div>Diff <code>[", self.winfo.name, "]</code> → Local Edits</div>", c||'No changes.' ].join(''); if(sbs) P.tweakSbsDiffs2(); F.message('Updated diff.'); self.tabs.switchToTab(self.e.tabs.diff); } }); return this; }; /** Saves the current wiki page and re-populates the editor with the saved state. If passed an argument, it is expected to be a function, which is called only if saving succeeds, after all other post-save processing. */ P.save = function callee(onSuccessCallback){ if(!affirmPageLoaded()) return this; else if(!this._isDirty){ F.error("There are no changes to save."); return this; } const content = this.wikiContent(); const self = this; callee.onload = function(w){ const oldWinfo = self.winfo; self.unstashContent(oldWinfo); self.dispatchEvent('wiki-page-loaded', w)/* will reset save buttons */; F.message("Saved page: ["+w.name+"]."); if('function'===typeof onSuccessCallback){ onSuccessCallback(); } }; const fd = new FormData(), w = P.winfo; fd.append('page',w.name); fd.append('mimetype', w.mimetype); fd.append('isnew', w.version ? 0 : 1); fd.append('content', P.wikiContent()); F.message( "Saving page..." ).fetch('wikiajax/save',{ payload: fd, responseType: 'json', onload: callee.onload }); return this; }; /** Updates P.winfo for certain state and stashes P.winfo, with the current content fetched via P.wikiContent(). If passed truthy AND the stash already has stashed content for the current page, only the stashed winfo record is updated, else both the winfo and content are updated. */ P.stashContentChange = function(onlyWinfo){ if(affirmPageLoaded(true)){ const wi = this.winfo; wi.mimetype = P.e.selectMimetype.value; if(onlyWinfo && $stash.hasStashedContent(wi)){ $stash.updateWinfo(wi); }else{ $stash.updateWinfo(wi, P.wikiContent()); } F.message("Stashed change(s) to page ["+wi.name+"]."); P.updatePageTitle(); $stash.prune(); this.previewNeedsUpdate = true; } return this; }; /** Removes any stashed state for the current P.winfo (if set) from F.storage. Returns this. */ P.unstashContent = function(){ const winfo = arguments[0] || this.winfo; if(winfo){ this.previewNeedsUpdate = true; $stash.unstash(winfo); //console.debug("Unstashed",winfo); F.message("Unstashed page ["+winfo.name+"]."); } return this; }; /** Clears all stashed file state from F.storage. Returns this. */ P.clearStash = function(){ $stash.clear(); return this; }; /** If stashed content for P.winfo exists, it is returned, else undefined is returned. */ P.contentFromStash = function(){ return affirmPageLoaded(true) ? $stash.stashedContent(this.winfo) : undefined; }; /** If a stashed version of the given winfo object exists (same filename/checkin values), return it, else return undefined. */ P.getStashedWinfo = function(winfo){ return $stash.getWinfo(winfo); }; })(window.fossil); |
Added src/fossil.popupwidget.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | (function(F/*fossil object*/){ /** A very basic tooltip-like widget. It's intended to be popped up to display basic information or basic user interaction components, e.g. a copy-to-clipboard button. Requires: fossil.bootstrap, fossil.dom */ const D = F.dom; /** Creates a new tooltip-like widget using the given options object. Options: .refresh: callback which is called just before the tooltip is revealed or moved. It must refresh the contents of the tooltip, if needed, by applying the content to/within this.e, which is the base DOM element for the tooltip (and is a child of document.body). If the contents are static and set up via the .init option then this callback is not needed. .adjustX: an optional callback which is called when the tooltip is to be displayed at a given position and passed the X viewport-relative coordinate. This routine must either return its argument as-is or return an adjusted value. The intent is to allow a given tooltip may be positioned more appropriately for a given context, if needed (noting that the desired position can, and probably should, be passed to the show() method instead). This class's API assumes that clients give it viewport-relative coordinates, and it will take care to translate those to page-relative, so this callback should not do so. .adjustY: the Y counterpart of adjustX. .init: optional callback called one time to initialize the state of the tooltip. This is called after the this.e has been created and added (initially hidden) to the DOM. If this is called, it is removed from the object immediately after it is called. All callback options are called with the PopupWidget object as their "this". .cssClass: optional CSS class, or list of classes, to apply to the new element. .style: optional object of properties to copy directly into the element's style object. The options passed to this constructor get normalized into a separate object which includes any default values for options not provided by the caller. That object is available this the resulting PopupWidget's options property. Default values for any options not provided by the caller are pulled from PopupWidget.defaultOptions, and modifying those affects all future calls to this method but has no effect on existing instances. Example: const tip = new fossil.PopupWidget({ init: function(){ // optionally populate DOM element this.e with the widget's // content. }, refresh: function(){ // (re)populate/refresh the contents of the main // wrapper element, this.e. } }); tip.show(50, 100); // ^^^ viewport-relative coordinates. See show() for other options. */ F.PopupWidget = function f(opt){ opt = F.mergeLastWins(f.defaultOptions,opt); this.options = opt; const e = this.e = D.addClass(D.div(), opt.cssClass); this.show(false); if(opt.style){ let k; for(k in opt.style){ if(opt.style.hasOwnProperty(k)) e.style[k] = opt.style[k]; } } D.append(document.body, e/*must be in the DOM for size calc. to work*/); D.copyStyle(e, opt.style); if(opt.init){ opt.init.call(this); delete opt.init; } }; /** Default options for the PopupWidget constructor. These values are used for any options not provided by the caller. Any changes made to this instace affect future calls to PopupWidget() but have no effect on existing instances. */ F.PopupWidget.defaultOptions = { cssClass: 'fossil-tooltip', style: undefined /*{optional properties copied as-is into element.style}*/, adjustX: (x)=>x, adjustY: (y)=>y, refresh: function(){}, init: undefined /* optional initialization function */ }; F.PopupWidget.prototype = { /** Returns true if the widget is currently being shown, else false. */ isShown: function(){return !this.e.classList.contains('hidden')}, /** Calls the refresh() method of the options object and returns this object. */ refresh: function(){ if(this.options.refresh){ this.options.refresh.call(this); } return this; }, /** Shows or hides the tooltip. Usages: (bool showIt) => hide it or reveal it at its last position. (x, y) => reveal/move it at/to the given relative-to-the-viewport position, which will be adjusted to make it page-relative. (DOM element) => reveal/move it at/to a position based on the the given element (adjusted slightly). For the latter two, this.options.adjustX() and adjustY() will be called to adjust it further. Returns this object. Sidebar: showing/hiding the widget is, as is conventional for this framework, done by removing/adding the 'hidden' CSS class to it, so that class must be defined appropriately. */ show: function(){ var x = undefined, y = undefined, showIt; if(2===arguments.length){ x = arguments[0]; y = arguments[1]; showIt = true; }else if(1===arguments.length){ if(arguments[0] instanceof HTMLElement){ const p = arguments[0]; const r = p.getBoundingClientRect(); x = r.x + r.x/5; y = r.y - r.height/2; showIt = true; }else{ showIt = !!arguments[0]; } } if(showIt){ this.refresh(); x = this.options.adjustX.call(this,x); y = this.options.adjustY.call(this,y); x += window.pageXOffset; y += window.pageYOffset; } if(showIt){ if('number'===typeof x && 'number'===typeof y){ this.e.style.left = x+"px"; this.e.style.top = y+"px"; } D.removeClass(this.e, 'hidden'); }else{ D.addClass(this.e, 'hidden'); delete this.e.style.removeProperty('left'); delete this.e.style.removeProperty('top'); } return this; }, hide: function(){return this.show(false)} }/*F.PopupWidget.prototype*/; /** Convenience wrapper around a PopupWidget which pops up a shared PopupWidget instance to show toast-style messages (commonly seen on Android). Its arguments may be anything suitable for passing to fossil.dom.append(), and each argument is first append()ed to the toast widget, then the widget is shown for F.toast.config.displayTimeMs milliseconds. This is called while a toast is currently being displayed, the first will be overwritten and the time until the message is hidden will be reset. The toast is always shown at the viewport-relative coordinates defined by the F.toast.config.position. The toaster's DOM element has the CSS classes fossil-tooltip and fossil-toast, so can be style via those. */ F.toast = function f(/*...*/){ if(!f.toast){ f.toast = function ff(argsObject){ if(!ff.toaster) ff.toaster = new F.PopupWidget({ cssClass: ['fossil-tooltip', 'fossil-toast'] }); if(f._timer) clearTimeout(f._timer); D.clearElement(ff.toaster.e); var i = 0; for( ; i < argsObject.length; ++i ){ D.append(ff.toaster.e, argsObject[i]); }; ff.toaster.show(f.config.position.x, f.config.position.y); f._timer = setTimeout(()=>ff.toaster.hide(), f.config.displayTimeMs); }; } f.toast(arguments); }; F.toast.config = { position: { x: 5, y: 5 /*viewport-relative, pixels*/ }, displayTimeMs: 2500 }; })(window.fossil); |
Changes to src/fossil.tabs.js.
1 2 3 4 5 6 7 | "use strict"; (function(F/*fossil object*/){ const E = (s)=>document.querySelector(s), EA = (s)=>document.querySelectorAll(s), D = F.dom; /** | | | > > > > > > > | > > > > > > > > > > | > > | > > > > | | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | "use strict"; (function(F/*fossil object*/){ const E = (s)=>document.querySelector(s), EA = (s)=>document.querySelectorAll(s), D = F.dom; /** Creates a TabManager. If passed a truthy first argument, it is passed to init(). If passed a truthy second argument, it must be an Object holding configuration options: { tabAccessKeys: boolean (=true) If true, tab buttons are assigned "accesskey" values equal to their 1-based tab number. } */ const TabManager = function(domElem, options){ this.e = {}; this.options = F.mergeLastWins(TabManager.defaultOptions , options); if(domElem) this.init(domElem); }; /** Default values for the options object passed to the TabManager constructor. Changing these affects the defaults of all TabManager instances instantiated after that point. */ TabManager.defaultOptions = { tabAccessKeys: true }; /** Internal helper to normalize a method argument to a tab element. arg may be a tab DOM element, a selector string, or an index into tabMgr.e.tabs.childNodes. Returns the corresponding tab element. */ const tabArg = function(arg,tabMgr){ if('string'===typeof arg) arg = E(arg); else if(tabMgr && 'number'===typeof arg && arg>=0){ arg = tabMgr.e.tabs.childNodes[arg]; } return arg; }; /** Sets sets the visibility of tab element e to on or off. e MUST be a TabManager tab element. */ const setVisible = function(e,yes){ D[yes ? 'removeClass' : 'addClass'](e, 'hidden'); }; TabManager.prototype = { /** Initializes the tabs associated with the given tab container (DOM element or selector for a single element). This must be called once before using any other member functions of a given instance, noting that the constructor will call this if it is passed an argument. The tab container must have an 'id' attribute. This function looks through the DOM for all elements which have data-tab-parent=thatId. For each one it creates a button to switch to that tab and moves the element into this.e.tabs, *possibly* injecting an intermediary element between this.e.tabs and the element. The label for each tab is set by the data-tab-label attribute of each element, defaulting to something not terribly useful. When it's done, it auto-selects the first tab unless a tab has a truthy numeric value in its data-tab-select attribute, in which case the last tab to have such a property is selected. |
︙ | ︙ | |||
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | }); return i>=0 ? this.e.tabBar.childNodes[i] : undefined; }, /** Adds the given DOM element or unique selector as the next tab in the tab container, adding a button to switch to the tab. Returns this object. */ addTab: function f(tab){ if(!f.click){ f.click = function(e){ e.target.$manager.switchToTab(e.target.$tab); }; } tab = tabArg(tab); tab.remove(); D.append(this.e.tabs, D.addClass(tab,'tab-panel')); | > > > > > > > > > | > > > | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | }); return i>=0 ? this.e.tabBar.childNodes[i] : undefined; }, /** Adds the given DOM element or unique selector as the next tab in the tab container, adding a button to switch to the tab. Returns this object. If this object's options include a truthy tabAccessKeys then each tab button gets assigned an accesskey attribute equal to its 1-based index in the tab list. e.g. key 1 is the first tab and key 5 is the 5th. Whether/how that accesskey is accessed is dependent on the browser and its OS: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey */ addTab: function f(tab){ if(!f.click){ f.click = function(e){ e.target.$manager.switchToTab(e.target.$tab); }; } tab = tabArg(tab); tab.remove(); D.append(this.e.tabs, D.addClass(tab,'tab-panel')); const tabCount = this.e.tabBar.childNodes.length+1; const lbl = tab.dataset.tabLabel || 'Tab #'+tabCount; const btn = D.addClass(D.append(D.span(), lbl), 'tab-button'); D.append(this.e.tabBar,btn); btn.$manager = this; btn.$tab = tab; if(this.options.tabAccessKeys){ D.attr(btn, 'accesskey', tabCount); } btn.addEventListener('click', f.click, false); return this; }, /** Internal. Fires a new CustomEvent to all listeners which have registered via this.addEventListener(). |
︙ | ︙ |
Changes to src/fusefs.c.
︙ | ︙ | |||
283 284 285 286 287 288 289 | static struct fuse_operations fusefs_methods = { .getattr = fusefs_getattr, .readdir = fusefs_readdir, .read = fusefs_read, }; /* | | | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | static struct fuse_operations fusefs_methods = { .getattr = fusefs_getattr, .readdir = fusefs_readdir, .read = fusefs_read, }; /* ** COMMAND: fusefs* ** ** Usage: %fossil fusefs [--debug] DIRECTORY ** ** This command uses the Fuse Filesystem (FuseFS) to mount a directory ** at DIRECTORY that contains the content of all check-ins in the ** repository. The names of files are DIRECTORY/checkins/VERSION/PATH ** where DIRECTORY is the root of the mount, VERSION is any valid |
︙ | ︙ |
Changes to src/glob.c.
︙ | ︙ | |||
161 162 163 164 165 166 167 168 169 170 171 172 173 174 | */ void glob_free(Glob *pGlob){ if( pGlob ){ fossil_free(pGlob->azPattern); fossil_free(pGlob); } } /* ** COMMAND: test-glob ** ** Usage: %fossil test-glob PATTERN STRING... ** ** PATTERN is a comma- and whitespace-separated list of optionally | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | */ void glob_free(Glob *pGlob){ if( pGlob ){ fossil_free(pGlob->azPattern); fossil_free(pGlob); } } /* ** Appends the given glob to the given buffer in the form of a ** JS/JSON-compatible array. It requires that pDest have been ** initialized. If pGlob is NULL or empty it emits [] (an empty ** array). */ void glob_render_json_to_blob(Glob *pGlob, Blob *pDest){ int i = 0; blob_append(pDest, "[", 1); for( ; pGlob && i < pGlob->nPattern; ++i ){ if(i){ blob_append(pDest, ",", 1); } blob_appendf(pDest, "%!j", pGlob->azPattern[i]); } blob_append(pDest, "]", 1); } /* ** Functionally equivalent to glob_render_json_to_blob() ** but outputs via cgi_print(). */ void glob_render_json_to_cgi(Glob *pGlob){ int i = 0; CX("["); for( ; pGlob && i < pGlob->nPattern; ++i ){ if(i){ CX(","); } CX("%!j", pGlob->azPattern[i]); } CX("]"); } /* ** COMMAND: test-glob ** ** Usage: %fossil test-glob PATTERN STRING... ** ** PATTERN is a comma- and whitespace-separated list of optionally |
︙ | ︙ |
Changes to src/graph.js.
︙ | ︙ | |||
777 778 779 780 781 782 783 | for(i=0; 1; i++){ var dataObj = document.getElementById("timeline-data-"+i); if(!dataObj) break; var txJson = dataObj.textContent || dataObj.innerText; var tx = JSON.parse(txJson); TimelineGraph(tx); } | | | 777 778 779 780 781 782 783 784 | for(i=0; 1; i++){ var dataObj = document.getElementById("timeline-data-"+i); if(!dataObj) break; var txJson = dataObj.textContent || dataObj.innerText; var tx = JSON.parse(txJson); TimelineGraph(tx); } }()); |
Changes to src/hook.c.
︙ | ︙ | |||
172 173 174 175 176 177 178 | while( db_step(&q)==SQLITE_ROW ){ blob_appendf(pOut, "%s %s\n", db_column_text(&q,0), db_column_text(&q,1)); } db_finalize(&q); } /* | | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | while( db_step(&q)==SQLITE_ROW ){ blob_appendf(pOut, "%s %s\n", db_column_text(&q,0), db_column_text(&q,1)); } db_finalize(&q); } /* ** COMMAND: hook* ** ** Usage: %fossil hook COMMAND ... ** ** Commands include: ** ** > fossil hook add --command COMMAND --type TYPE --sequence NUMBER ** |
︙ | ︙ |
Changes to src/import.c.
︙ | ︙ | |||
1592 1593 1594 1595 1596 1597 1598 | db_finalize(&cpyPath); db_finalize(&cpyRoot); db_finalize(&revSrc); fossil_print(" Done!\n"); } /* | | | 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 | db_finalize(&cpyPath); db_finalize(&cpyRoot); db_finalize(&revSrc); fossil_print(" Done!\n"); } /* ** COMMAND: import* ** ** Usage: %fossil import ?--git? ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE? ** or: %fossil import --svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE? ** ** Read interchange format generated by another VCS and use it to ** construct a new Fossil repository named by the NEW-REPOSITORY ** argument. If no input file is supplied the interchange format |
︙ | ︙ |
Changes to src/info.c.
︙ | ︙ | |||
189 190 191 192 193 194 195 | ** file in a checkout. ** ** Options: ** ** -R|--repository FILE Extract info from repository FILE ** -v|--verbose Show extra information about repositories ** | | | 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | ** file in a checkout. ** ** Options: ** ** -R|--repository FILE Extract info from repository FILE ** -v|--verbose Show extra information about repositories ** ** See also: [[annotate]], [[artifact]], [[finfo]], [[timeline]] */ void info_cmd(void){ i64 fsize; int verboseFlag = find_option("verbose","v",0)!=0; if( !verboseFlag ){ verboseFlag = find_option("detail","l",0)!=0; /* deprecated */ } |
︙ | ︙ | |||
439 440 441 442 443 444 445 | } /* ** Generate javascript to enhance HTML diffs. */ void append_diff_javascript(int sideBySide){ if( !sideBySide ) return; | | | 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 | } /* ** Generate javascript to enhance HTML diffs. */ void append_diff_javascript(int sideBySide){ if( !sideBySide ) return; builtin_request_js("sbsdiff.js"); } /* ** Construct an appropriate diffFlag for text_diff() based on query ** parameters and the to boolean arguments. */ u64 construct_diff_flags(int diffType){ |
︙ | ︙ | |||
2009 2010 2011 2012 2013 2014 2015 | } } manifest_destroy(pManifest); return rid; } /* | | > | > > > > > > > > > > > > > > > > > > > | > | | | > | | | < | < < < < < < > > | < | | < < > > > > > > > > > > > > > | > > > > | | < < < < | | < > > > | < < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 | } } manifest_destroy(pManifest); return rid; } /* ** The "z" argument is a string that contains the text of a source ** code file and nZ is its length in bytes. This routine appends that ** text to the HTTP reply with line numbering. ** ** zName is the content's file name, if any (it may be NULL). If that ** name contains a '.' then the part after the final '.' is used as ** the X part of a "language-X" CSS class on the generated CODE block. ** ** zLn is the ?ln= parameter for the HTTP query. If there is an argument, ** then highlight that line number and scroll to it once the page loads. ** If there are two line numbers, highlight the range of lines. ** Multiple ranges can be highlighed by adding additional line numbers ** separated by a non-digit character (also not one of [-,.]). */ void output_text_with_line_numbers( const char *z, int nZ, const char *zName, const char *zLn ){ int iStart, iEnd; /* Start and end of region to highlight */ int n = 0; /* Current line number */ int i = 0; /* Loop index */ int iTop = 0; /* Scroll so that this line is on top of screen. */ int nLine = 0; /* content line count */ int nSpans = 0; /* number of distinct zLn spans */ const char *zExt = file_extension(zName); Stmt q; iStart = iEnd = atoi(zLn); db_multi_exec( "CREATE TEMP TABLE lnos(iStart INTEGER PRIMARY KEY, iEnd INTEGER)"); if( iStart>0 ){ do{ while( fossil_isdigit(zLn[i]) ) i++; if( zLn[i]==',' || zLn[i]=='-' || zLn[i]=='.' ){ i++; while( zLn[i]=='.' ){ i++; } iEnd = atoi(&zLn[i]); while( fossil_isdigit(zLn[i]) ) i++; } while( fossil_isdigit(zLn[i]) ) i++; if( iEnd<iStart ) iEnd = iStart; db_multi_exec( "INSERT OR REPLACE INTO lnos VALUES(%d,%d)", iStart, iEnd ); ++nSpans; iStart = iEnd = atoi(&zLn[i++]); }while( zLn[i] && iStart && iEnd ); } /*cgi_printf("<!-- ln span count=%d -->", nSpans);*/ cgi_append_content("<table class='numbered-lines'><tbody>" "<tr><td class='line-numbers'>", -1); iStart = iEnd = 0; count_lines(z, nZ, &nLine); for( n=1 ; n<=nLine; ++n ){ const char * zAttr = ""; const char * zId = ""; if(nSpans>0 && iEnd==0){/*Grab the next range of zLn marking*/ db_prepare(&q, "SELECT iStart, iEnd FROM lnos " "WHERE iStart >= %d ORDER BY iStart", n); if( db_step(&q)==SQLITE_ROW ){ iStart = db_column_int(&q, 0); iEnd = db_column_int(&q, 1); if(!iTop){ iTop = iStart - 15 + (iEnd-iStart)/4; if( iTop>iStart - 2 ) iTop = iStart-2; } }else{ /* Note that overlapping multi-spans, e.g. 10-15+12-20, can cause us to miss a row. */ iStart = iEnd = 0; } db_finalize(&q); --nSpans; /*cgi_printf("<!-- iStart=%d, iEnd=%d -->", iStart, iEnd);*/ } if(n==iTop) { zId = " id='scrollToMe'"; } if(n==iStart){/*Figure out which CSS class(es) this line needs...*/ if(n==iEnd){ zAttr = " class='selected-line start end'"; iEnd = 0; }else{ zAttr = " class='selected-line start'"; } iStart = 0; }else if(n==iEnd){ zAttr = " class='selected-line end'"; iEnd = 0; }else if( n>iStart && n<iEnd ){ zAttr = " class='selected-line'"; } cgi_printf("<span%s%s>%6d</span>", zId, zAttr, n); } cgi_append_content("</td><td class='file-content'><pre>",-1); if(zExt && *zExt){ cgi_printf("<code class='language-%h'>",zExt); }else{ cgi_append_content("<code>", -1); } cgi_printf("%z", htmlize(z, nZ)); CX("</code></pre></td></tr></tbody></table>\n"); if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ builtin_request_js("scroll.js"); } style_emit_fossil_js_apis(0, "dom", "copybutton", "popupwidget", "numbered-lines", 0); } /* ** COMMAND: test-line-numbers ** ** Usage: %fossil test-line-numbers FILE ?LN-SPEC? ** */ void cmd_test_line_numbers(void){ Blob content = empty_blob; const char * zLn = ""; const char * zFilename = 0; if(g.argc < 3){ usage("FILE"); }else if(g.argc>3){ zLn = g.argv[3]; } db_find_and_open_repository(0,0); zFilename = g.argv[2]; fossil_print("%s %s\n", zFilename, zLn); blob_read_from_file(&content, zFilename, ExtFILE); output_text_with_line_numbers(blob_str(&content), blob_size(&content), zFilename, zLn); blob_reset(&content); fossil_print("%b\n", cgi_output_blob()); } /* ** WEBPAGE: artifact ** WEBPAGE: file ** WEBPAGE: whatis ** ** Typical usage: |
︙ | ︙ | |||
2385 2386 2387 2388 2389 2390 2391 | }else{ style_submenu_element("Hex", "%s/hexdump?name=%s", g.zTop, zUuid); if( zLn==0 || atoi(zLn)==0 ){ style_submenu_checkbox("ln", "Line Numbers", 0, 0); } blob_to_utf8_no_bom(&content, 0); zMime = mimetype_from_content(&content); | | | | > | | 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 | }else{ style_submenu_element("Hex", "%s/hexdump?name=%s", g.zTop, zUuid); if( zLn==0 || atoi(zLn)==0 ){ style_submenu_checkbox("ln", "Line Numbers", 0, 0); } blob_to_utf8_no_bom(&content, 0); zMime = mimetype_from_content(&content); @ <blockquote class="file-content"> if( zMime==0 ){ const char *z, *zFileName, *zExt; z = blob_str(&content); zFileName = db_text(0, "SELECT name FROM mlink, filename" " WHERE filename.fnid=mlink.fnid" " AND mlink.fid=%d", rid); zExt = file_extension(zFileName); if( zLn ){ output_text_with_line_numbers(z, blob_size(&content), zFileName, zLn); }else if( zExt && zExt[1] ){ @ <pre> @ <code class="language-%s(zExt)">%h(z)</code> @ </pre> }else{ @ <pre> @ %h(z) @ </pre> } }else if( strncmp(zMime, "image/", 6)==0 ){ |
︙ | ︙ | |||
3111 3112 3113 3114 3115 3116 3117 | @ <input type="submit" name="preview" value="Preview" /> if( P("preview") ){ @ <input type="submit" name="apply" value="Apply Changes" /> } @ </td></tr> @ </table> @ </div></form> | | | 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 | @ <input type="submit" name="preview" value="Preview" /> if( P("preview") ){ @ <input type="submit" name="apply" value="Apply Changes" /> } @ </td></tr> @ </table> @ </div></form> builtin_request_js("ci_edit.js"); style_footer(); } /* ** Prepare an ammended commit comment. Let the user modify it using the ** editor specified in the global_config table or either ** the VISUAL or EDITOR environment variable. |
︙ | ︙ |
Changes to src/json_config.c.
︙ | ︙ | |||
59 60 61 62 63 64 65 66 67 68 69 70 71 72 | { "header", CONFIGSET_SKIN }, { "footer", CONFIGSET_SKIN }, { "details", CONFIGSET_SKIN }, { "logo-mimetype", CONFIGSET_SKIN }, { "logo-image", CONFIGSET_SKIN }, { "background-mimetype", CONFIGSET_SKIN }, { "background-image", CONFIGSET_SKIN }, { "timeline-block-markup", CONFIGSET_SKIN }, { "timeline-max-comment", CONFIGSET_SKIN }, { "timeline-plaintext", CONFIGSET_SKIN }, { "adunit", CONFIGSET_SKIN }, { "adunit-omit-if-admin", CONFIGSET_SKIN }, { "adunit-omit-if-user", CONFIGSET_SKIN }, | > > | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | { "header", CONFIGSET_SKIN }, { "footer", CONFIGSET_SKIN }, { "details", CONFIGSET_SKIN }, { "logo-mimetype", CONFIGSET_SKIN }, { "logo-image", CONFIGSET_SKIN }, { "background-mimetype", CONFIGSET_SKIN }, { "background-image", CONFIGSET_SKIN }, { "icon-mimetype", CONFIGSET_SKIN }, { "icon-image", CONFIGSET_SKIN }, { "timeline-block-markup", CONFIGSET_SKIN }, { "timeline-max-comment", CONFIGSET_SKIN }, { "timeline-plaintext", CONFIGSET_SKIN }, { "adunit", CONFIGSET_SKIN }, { "adunit-omit-if-admin", CONFIGSET_SKIN }, { "adunit-omit-if-user", CONFIGSET_SKIN }, |
︙ | ︙ |
Changes to src/login.c.
︙ | ︙ | |||
750 751 752 753 754 755 756 | @ <div class="captcha"><table class="captcha"><tr><td>\ @ <pre class="captcha"> @ %h(zCaptcha) @ </pre></td></tr></table> if( bAutoCaptcha ) { @ <input type="button" value="Fill out captcha" id='autofillButton' \ @ data-af='%s(zDecoded)' /> | | | 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 | @ <div class="captcha"><table class="captcha"><tr><td>\ @ <pre class="captcha"> @ %h(zCaptcha) @ </pre></td></tr></table> if( bAutoCaptcha ) { @ <input type="button" value="Fill out captcha" id='autofillButton' \ @ data-af='%s(zDecoded)' /> builtin_request_js("login.js"); } @ </div> free(zCaptcha); } @ </form> } if( login_is_individual() ){ |
︙ | ︙ | |||
1589 1590 1591 1592 1593 1594 1595 | zErr = "This User ID is already taken. Choose something different."; }else if( /* If the email is found anywhere in USER.INFO... */ db_exists("SELECT 1 FROM user WHERE info LIKE '%%%q%%'", zEAddr) || /* Or if the email is a verify subscriber email with an associated ** user... */ | > | | | | 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 | zErr = "This User ID is already taken. Choose something different."; }else if( /* If the email is found anywhere in USER.INFO... */ db_exists("SELECT 1 FROM user WHERE info LIKE '%%%q%%'", zEAddr) || /* Or if the email is a verify subscriber email with an associated ** user... */ (alert_tables_exist() && db_exists( "SELECT 1 FROM subscriber WHERE semail=%Q AND suname IS NOT NULL" " AND sverified",zEAddr)) ){ iErrLine = 3; zErr = "This email address is already claimed by another user"; }else{ /* If all of the tests above have passed, that means that the submitted ** form contains valid data and we can proceed to create the new login */ Blob sql; |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 | ** ** redirect: REPO URL Extract the "name" query parameter and search ** REPO for a check-in or ticket that matches the ** value of "name", then redirect to URL. There ** can be multiple "redirect:" lines that are ** processed in order. If the REPO is "*", then ** an unconditional redirect to URL is taken. ** ** Most CGI files contain only a "repository:" line. It is uncommon to ** use any other option. ** | > > > > | | 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 | ** ** redirect: REPO URL Extract the "name" query parameter and search ** REPO for a check-in or ticket that matches the ** value of "name", then redirect to URL. There ** can be multiple "redirect:" lines that are ** processed in order. If the REPO is "*", then ** an unconditional redirect to URL is taken. ** ** jsmode: VALUE Specifies the delivery mode for JavaScript ** files. See the help text for the --jsmode ** flag of the http command. ** ** Most CGI files contain only a "repository:" line. It is uncommon to ** use any other option. ** ** See also: [[http]], [[server]], [[winsrv]] */ void cmd_cgi(void){ const char *zFile; const char *zNotFound = 0; char **azRedirect = 0; /* List of repositories to redirect to */ int nRedirect = 0; /* Number of entries in azRedirect */ Glob *pFileGlob = 0; /* Pattern for files */ |
︙ | ︙ | |||
2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 | ** name of the subdirectory under the skins/ directory that holds ** the elements of the built-in skin. If LABEL does not match, ** this directive is a silent no-op. */ skin_use_alternative(blob_str(&value)); blob_reset(&value); continue; } if( blob_eq(&key, "cgi-debug:") && blob_token(&line, &value) ){ /* cgi-debug: FILENAME ** ** Causes output from cgi_debug() and CGIDEBUG(()) calls to go ** into FILENAME. Useful for debugging CGI configuration problems. */ | > > > > > > > > > > > > > > > | 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 | ** name of the subdirectory under the skins/ directory that holds ** the elements of the built-in skin. If LABEL does not match, ** this directive is a silent no-op. */ skin_use_alternative(blob_str(&value)); blob_reset(&value); continue; } if( blob_eq(&key, "jsmode:") && blob_token(&line, &value) ){ /* jsmode: MODE ** ** Change how JavaScript resources are delivered with each HTML ** page. MODE is "inline" to put all JS inline, or "separate" to ** cause each JS file to be requested using a separate HTTP request, ** or "bundled" to have all JS files to be fetched with a single ** auxiliary HTTP request. Noting, however, that "single" might ** actually mean more than one, depending on the script-timing ** requirements of any given page. */ builtin_set_js_delivery_mode(blob_str(&value),0); blob_reset(&value); continue; } if( blob_eq(&key, "cgi-debug:") && blob_token(&line, &value) ){ /* cgi-debug: FILENAME ** ** Causes output from cgi_debug() and CGIDEBUG(()) calls to go ** into FILENAME. Useful for debugging CGI configuration problems. */ |
︙ | ︙ | |||
2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 | ** --baseurl URL base URL (useful with reverse proxies) ** --extroot DIR document root for the /ext extension mechanism ** --files GLOB comma-separate glob patterns for static file to serve ** --host NAME specify hostname of the server ** --https signal a request coming in via https ** --in FILE Take input from FILE instead of standard input ** --ipaddr ADDR Assume the request comes from the given IP address ** --localauth enable automatic login for local connections ** --nocompress do not compress HTTP replies ** --nodelay omit backoffice processing if it would delay process exit ** --nojail drop root privilege but do not enter the chroot jail ** --nossl signal that no SSL connections are available ** --notfound URL use URL as "HTTP 404, object not found" page. ** --out FILE write results to FILE instead of to standard output ** --repolist If REPOSITORY is directory, URL "/" lists all repos ** --scgi Interpret input as SCGI rather than HTTP ** --skin LABEL Use override skin LABEL ** --th-trace trace TH1 execution (for debugging purposes) ** --usepidkey Use saved encryption key from parent process. This is ** only necessary when using SEE on Windows. ** | > > > > > > > > > > > > > | > | 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 | ** --baseurl URL base URL (useful with reverse proxies) ** --extroot DIR document root for the /ext extension mechanism ** --files GLOB comma-separate glob patterns for static file to serve ** --host NAME specify hostname of the server ** --https signal a request coming in via https ** --in FILE Take input from FILE instead of standard input ** --ipaddr ADDR Assume the request comes from the given IP address ** --jsmode MODE Determine how JavaScript is delivered with pages. ** Mode can be one of: ** inline All JavaScript is inserted inline at ** one or more points in the HTML file. ** separate Separate HTTP requests are made for ** each JavaScript file. ** bundled Groups JavaScript files into one or ** more bundled requests which ** concatenate scripts together. ** Depending on the needs of any given page, inline ** and bundled modes might result in a single ** amalgamated script or several, but both approaches ** result in fewer HTTP requests than the separate mode. ** --localauth enable automatic login for local connections ** --nocompress do not compress HTTP replies ** --nodelay omit backoffice processing if it would delay process exit ** --nojail drop root privilege but do not enter the chroot jail ** --nossl signal that no SSL connections are available ** --notfound URL use URL as "HTTP 404, object not found" page. ** --out FILE write results to FILE instead of to standard output ** --repolist If REPOSITORY is directory, URL "/" lists all repos ** --scgi Interpret input as SCGI rather than HTTP ** --skin LABEL Use override skin LABEL ** --th-trace trace TH1 execution (for debugging purposes) ** --usepidkey Use saved encryption key from parent process. This is ** only necessary when using SEE on Windows. ** ** See also: [[cgi]], [[server]], [[winsrv]] */ void cmd_http(void){ const char *zIpAddr = 0; const char *zNotFound; const char *zHost; const char *zAltBase; const char *zFileGlob; const char *zInFile; const char *zOutFile; int useSCGI; int noJail; int allowRepoList; Th_InitTraceLog(); builtin_set_js_delivery_mode(find_option("jsmode",0,1),0); /* The winhttp module passes the --files option as --files-urlenc with ** the argument being URL encoded, to avoid wildcard expansion in the ** shell. This option is for internal use and is undocumented. */ zFileGlob = find_option("files-urlenc",0,1); if( zFileGlob ){ |
︙ | ︙ | |||
2572 2573 2574 2575 2576 2577 2578 | } /* ** Note that the following command is used by ssh:// processing. ** ** COMMAND: test-http ** | | | 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 | } /* ** Note that the following command is used by ssh:// processing. ** ** COMMAND: test-http ** ** Works like the [[http]] command but gives setup permission to all users. ** ** Options: ** --th-trace trace TH1 execution (for debugging purposes) ** --usercap CAP user capability string. (Default: "sx") ** */ void cmd_test_http(void){ |
︙ | ︙ | |||
2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 | ** --create Create a new REPOSITORY if it does not already exist ** --extroot DIR Document root for the /ext extension mechanism ** --files GLOBLIST Comma-separated list of glob patterns for static files ** --localauth enable automatic login for requests from localhost ** --localhost listen on 127.0.0.1 only (always true for "ui") ** --https Indicates that the input is coming through a reverse ** proxy that has already translated HTTPS into HTTP. ** --max-latency N Do not let any single HTTP request run for more than N ** seconds (only works on unix) ** --nocompress Do not compress HTTP replies ** --nojail Drop root privileges but do not enter the chroot jail ** --nossl signal that no SSL connections are available (Always ** set by default for the "ui" command) ** --notfound URL Redirect ** --page PAGE Start "ui" on PAGE. ex: --page "timeline?y=ci" ** -P|--port TCPPORT listen to request on port TCPPORT ** --th-trace trace TH1 execution (for debugging purposes) ** --repolist If REPOSITORY is dir, URL "/" lists repos. ** --scgi Accept SCGI rather than HTTP ** --skin LABEL Use override skin LABEL ** --usepidkey Use saved encryption key from parent process. This is ** only necessary when using SEE on Windows. ** | > > > > > > > > > > > > | | 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 | ** --create Create a new REPOSITORY if it does not already exist ** --extroot DIR Document root for the /ext extension mechanism ** --files GLOBLIST Comma-separated list of glob patterns for static files ** --localauth enable automatic login for requests from localhost ** --localhost listen on 127.0.0.1 only (always true for "ui") ** --https Indicates that the input is coming through a reverse ** proxy that has already translated HTTPS into HTTP. ** --jsmode MODE Determine how JavaScript is delivered with pages. ** Mode can be one of: ** inline All JavaScript is inserted inline at ** the end of the HTML file. ** separate Separate HTTP requests are made for ** each JavaScript file. ** bundled One single separate HTTP fetches all ** JavaScript concatenated together. ** Depending on the needs of any given page, inline ** and bundled modes might result in a single ** amalgamated script or several, but both approaches ** result in fewer HTTP requests than the separate mode. ** --max-latency N Do not let any single HTTP request run for more than N ** seconds (only works on unix) ** --nocompress Do not compress HTTP replies ** --nojail Drop root privileges but do not enter the chroot jail ** --nossl signal that no SSL connections are available (Always ** set by default for the "ui" command) ** --notfound URL Redirect ** --page PAGE Start "ui" on PAGE. ex: --page "timeline?y=ci" ** -P|--port TCPPORT listen to request on port TCPPORT ** --th-trace trace TH1 execution (for debugging purposes) ** --repolist If REPOSITORY is dir, URL "/" lists repos. ** --scgi Accept SCGI rather than HTTP ** --skin LABEL Use override skin LABEL ** --usepidkey Use saved encryption key from parent process. This is ** only necessary when using SEE on Windows. ** ** See also: [[cgi]], [[http]], [[winsrv]] */ void cmd_webserver(void){ int iPort, mxPort; /* Range of TCP ports allowed */ const char *zPort; /* Value of the --port option */ const char *zBrowser; /* Name of web browser program */ char *zBrowserCmd = 0; /* Command to launch the web browser */ int isUiCmd; /* True if command is "ui", not "server' */ |
︙ | ︙ | |||
2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 | zStopperFile = find_option("stopper", 0, 1); #endif if( g.zErrlog==0 ){ g.zErrlog = "-"; } g.zExtRoot = find_option("extroot",0,1); zFileGlob = find_option("files-urlenc",0,1); if( zFileGlob ){ char *z = mprintf("%s", zFileGlob); dehttpize(z); zFileGlob = z; }else{ zFileGlob = find_option("files",0,1); | > | 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 | zStopperFile = find_option("stopper", 0, 1); #endif if( g.zErrlog==0 ){ g.zErrlog = "-"; } g.zExtRoot = find_option("extroot",0,1); builtin_set_js_delivery_mode(find_option("jsmode",0,1),0); zFileGlob = find_option("files-urlenc",0,1); if( zFileGlob ){ char *z = mprintf("%s", zFileGlob); dehttpize(z); zFileGlob = z; }else{ zFileGlob = find_option("files",0,1); |
︙ | ︙ |
Changes to src/main.mk.
︙ | ︙ | |||
152 153 154 155 156 157 158 | $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/webmail.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winfile.c \ $(SRCDIR)/winhttp.c \ | < | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/webmail.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winfile.c \ $(SRCDIR)/winhttp.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ $(SRCDIR)/zip.c EXTRA_FILES = \ $(SRCDIR)/../skins/aht/details.txt \ $(SRCDIR)/../skins/ardoise/css.txt \ |
︙ | ︙ | |||
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | $(SRCDIR)/ci_edit.js \ $(SRCDIR)/copybtn.js \ $(SRCDIR)/default.css \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/forum.js \ $(SRCDIR)/fossil.bootstrap.js \ $(SRCDIR)/fossil.confirmer.js \ $(SRCDIR)/fossil.dom.js \ $(SRCDIR)/fossil.fetch.js \ $(SRCDIR)/fossil.page.fileedit.js \ $(SRCDIR)/fossil.page.forumpost.js \ $(SRCDIR)/fossil.storage.js \ $(SRCDIR)/fossil.tabs.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ | > > > > | 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | $(SRCDIR)/ci_edit.js \ $(SRCDIR)/copybtn.js \ $(SRCDIR)/default.css \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/forum.js \ $(SRCDIR)/fossil.bootstrap.js \ $(SRCDIR)/fossil.confirmer.js \ $(SRCDIR)/fossil.copybutton.js \ $(SRCDIR)/fossil.dom.js \ $(SRCDIR)/fossil.fetch.js \ $(SRCDIR)/fossil.numbered-lines.js \ $(SRCDIR)/fossil.page.fileedit.js \ $(SRCDIR)/fossil.page.forumpost.js \ $(SRCDIR)/fossil.page.wikiedit.js \ $(SRCDIR)/fossil.popupwidget.js \ $(SRCDIR)/fossil.storage.js \ $(SRCDIR)/fossil.tabs.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ |
︙ | ︙ | |||
255 256 257 258 259 260 261 262 263 264 265 266 267 268 | $(SRCDIR)/sounds/b.wav \ $(SRCDIR)/sounds/c.wav \ $(SRCDIR)/sounds/d.wav \ $(SRCDIR)/sounds/e.wav \ $(SRCDIR)/sounds/f.wav \ $(SRCDIR)/style.admin_log.css \ $(SRCDIR)/style.fileedit.css \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/ajax_.c \ | > | 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | $(SRCDIR)/sounds/b.wav \ $(SRCDIR)/sounds/c.wav \ $(SRCDIR)/sounds/d.wav \ $(SRCDIR)/sounds/e.wav \ $(SRCDIR)/sounds/f.wav \ $(SRCDIR)/style.admin_log.css \ $(SRCDIR)/style.fileedit.css \ $(SRCDIR)/style.wikiedit.css \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/ajax_.c \ |
︙ | ︙ | |||
400 401 402 403 404 405 406 | $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/webmail_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winfile_.c \ $(OBJDIR)/winhttp_.c \ | < | 404 405 406 407 408 409 410 411 412 413 414 415 416 417 | $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/webmail_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winfile_.c \ $(OBJDIR)/winhttp_.c \ $(OBJDIR)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/ajax.o \ |
︙ | ︙ | |||
546 547 548 549 550 551 552 | $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/webmail.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winfile.o \ $(OBJDIR)/winhttp.o \ | < | 549 550 551 552 553 554 555 556 557 558 559 560 561 562 | $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/webmail.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winfile.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ $(OBJDIR)/zip.o all: $(OBJDIR) $(APPNAME) install: all mkdir -p $(INSTALLDIR) |
︙ | ︙ | |||
596 597 598 599 600 601 602 | # # TESTFLAGS can also include names of specific test files to limit # the run to just those test cases. # test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) | | > > > | 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 | # # TESTFLAGS can also include names of specific test files to limit # the run to just those test cases. # test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h $(OBJDIR)/phony.h: # Force rebuild of VERSION.h every time we run "make" # Setup the options used to compile the included SQLite library. SQLITE_OPTIONS = -DNDEBUG=1 \ -DSQLITE_DQS=0 \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_DEFAULT_MEMSTATUS=0 \ -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ |
︙ | ︙ | |||
879 880 881 882 883 884 885 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ | < | 884 885 886 887 888 889 890 891 892 893 894 895 896 897 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ $(SRCDIR)/sqlite3.h \ $(SRCDIR)/th.h \ $(OBJDIR)/VERSION.h touch $(OBJDIR)/headers |
︙ | ︙ | |||
2010 2011 2012 2013 2014 2015 2016 | $(OBJDIR)/translate $(SRCDIR)/winhttp.c >$@ $(OBJDIR)/winhttp.o: $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h: $(OBJDIR)/headers | < < < < < < < < | 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 | $(OBJDIR)/translate $(SRCDIR)/winhttp.c >$@ $(OBJDIR)/winhttp.o: $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h: $(OBJDIR)/headers $(OBJDIR)/xfer_.c: $(SRCDIR)/xfer.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/xfer.c >$@ $(OBJDIR)/xfer.o: $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h: $(OBJDIR)/headers |
︙ | ︙ |
Changes to src/makemake.tcl.
︙ | ︙ | |||
162 163 164 165 166 167 168 | verify vfile webmail wiki wikiformat winfile winhttp | < | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | verify vfile webmail wiki wikiformat winfile winhttp xfer xfersetup zip http_ssl } # Additional resource files that get built into the executable. |
︙ | ︙ | |||
368 369 370 371 372 373 374 | # # TESTFLAGS can also include names of specific test files to limit # the run to just those test cases. # test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) | | > > > | 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 | # # TESTFLAGS can also include names of specific test files to limit # the run to just those test cases. # test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \ $(SRCDIR)/../manifest \ $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h $(OBJDIR)/phony.h: # Force rebuild of VERSION.h every time we run "make" # Setup the options used to compile the included SQLite library. SQLITE_OPTIONS = <<<SQLITE_OPTIONS>>> # Setup the options used to compile the included SQLite shell. SHELL_OPTIONS = <<<SHELL_OPTIONS>>> # Setup the options used to compile the included miniz library. |
︙ | ︙ | |||
1079 1080 1081 1082 1083 1084 1085 | # WARNING. DANGER. Running the test suite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) | | > > > | 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 | # WARNING. DANGER. Running the test suite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) $(OBJDIR)/phony.h $(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@ $(OBJDIR)/phony.h: # Force rebuild of VERSION.h every time "make" is run # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set # to 1. If it is set to 1, then there is no need to build or link # the sqlite3.o object. Instead, the system SQLite will be linked # using -lsqlite3. SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o SQLITE3_OBJ.1 = SQLITE3_OBJ. = $(SQLITE3_OBJ.0) |
︙ | ︙ | |||
1905 1906 1907 1908 1909 1910 1911 | "$(OX)\th_tcl$O" : "$(SRCDIR)\th_tcl.c" $(TCC) /Fo$@ /Fd$(@D)\ -c $** "$(OX)\miniz$O" : "$(SRCDIR)\miniz.c" $(TCC) /Fo$@ /Fd$(@D)\ -c $(MINIZ_OPTIONS) $** | | > | > > | 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 | "$(OX)\th_tcl$O" : "$(SRCDIR)\th_tcl.c" $(TCC) /Fo$@ /Fd$(@D)\ -c $** "$(OX)\miniz$O" : "$(SRCDIR)\miniz.c" $(TCC) /Fo$@ /Fd$(@D)\ -c $(MINIZ_OPTIONS) $** "$(OX)\VERSION.h" : "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" "$(B)\phony.h" "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" > $@ "$(B)\phony.h" : rem Force rebuild of VERSION.h whenever nmake is run "$(OX)\cson_amalgamation$O" : "$(SRCDIR)\cson_amalgamation.c" $(TCC) /Fo$@ /Fd$(@D)\ -c $** "$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC) $** > $@ |
︙ | ︙ |
Changes to src/mkbuiltin.c.
︙ | ︙ | |||
60 61 62 63 64 65 66 67 68 69 70 71 72 73 | } got = fread(z, 1, nByte, in); fclose(in); z[got] = 0; return z; } /* ** Try to compress a javascript file by removing unnecessary whitespace. ** ** Warning: This compression routine does not necessarily work for any ** arbitrary Javascript source file. But it should work ok for the ** well-behaved source files in this project. */ | > | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | } got = fread(z, 1, nByte, in); fclose(in); z[got] = 0; return z; } #ifndef FOSSIL_DEBUG /* ** Try to compress a javascript file by removing unnecessary whitespace. ** ** Warning: This compression routine does not necessarily work for any ** arbitrary Javascript source file. But it should work ok for the ** well-behaved source files in this project. */ |
︙ | ︙ | |||
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | while( j>0 && (z[j-1]==' ' || z[j-1]=='\t') ){ j--; } for(k=i+2; k<n && z[k]!='\n'; k++){} i = k-1; continue; } } if( c=='\n' ){ while( j>0 && isspace(z[j-1]) ) j--; z[j++] = '\n'; while( i+1<n && isspace(z[i+1]) ) i++; continue; } z[j++] = c; } z[j] = 0; *pn = j; } /* ** There is an instance of the following for each file translated. */ typedef struct Resource Resource; struct Resource { char *zName; | > > | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | while( j>0 && (z[j-1]==' ' || z[j-1]=='\t') ){ j--; } for(k=i+2; k<n && z[k]!='\n'; k++){} i = k-1; continue; } } if( c=='\n' ){ if( j==0 ) continue; while( j>0 && isspace(z[j-1]) ) j--; z[j++] = '\n'; while( i+1<n && isspace(z[i+1]) ) i++; continue; } z[j++] = c; } z[j] = 0; *pn = j; } #endif /* FOSSIL_DEBUG */ /* ** There is an instance of the following for each file translated. */ typedef struct Resource Resource; struct Resource { char *zName; |
︙ | ︙ | |||
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | ResourceList resList; Resource *aRes; int nRes; unsigned char *pData; int nErr = 0; int nSkip; int nPrefix = 0; int nName; if( argc==1 ){ fprintf(stderr, "usage\t:%s " "[--prefix path] [--reslist file] [resource-file1 ...]\n", argv[0] ); return 1; | > > | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | ResourceList resList; Resource *aRes; int nRes; unsigned char *pData; int nErr = 0; int nSkip; int nPrefix = 0; #ifndef FOSSIL_DEBUG int nName; #endif if( argc==1 ){ fprintf(stderr, "usage\t:%s " "[--prefix path] [--reslist file] [resource-file1 ...]\n", argv[0] ); return 1; |
︙ | ︙ | |||
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 | /* Skip initial lines beginning with # */ nSkip = 0; while( pData[nSkip]=='#' ){ while( pData[nSkip]!=0 && pData[nSkip]!='\n' ){ nSkip++; } if( pData[nSkip]=='\n' ) nSkip++; } /* Compress javascript source files */ nName = (int)strlen(aRes[i].zName); if( (nName>3 && strcmp(&aRes[i].zName[nName-3],".js")==0) || (nName>7 && strcmp(&aRes[i].zName[nName-7], "/js.txt")==0) ){ int x = sz-nSkip; compressJavascript(pData+nSkip, &x); sz = x + nSkip; } aRes[i].nByte = sz - nSkip; aRes[i].idx = i; printf("/* Content of file %s */\n", aRes[i].zName); printf("static const unsigned char bidata%d[%d] = {\n ", i, sz+1-nSkip); for(j=nSkip, n=0; j<=sz; j++){ | > > | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 | /* Skip initial lines beginning with # */ nSkip = 0; while( pData[nSkip]=='#' ){ while( pData[nSkip]!=0 && pData[nSkip]!='\n' ){ nSkip++; } if( pData[nSkip]=='\n' ) nSkip++; } #ifndef FOSSIL_DEBUG /* Compress javascript source files */ nName = (int)strlen(aRes[i].zName); if( (nName>3 && strcmp(&aRes[i].zName[nName-3],".js")==0) || (nName>7 && strcmp(&aRes[i].zName[nName-7], "/js.txt")==0) ){ int x = sz-nSkip; compressJavascript(pData+nSkip, &x); sz = x + nSkip; } #endif aRes[i].nByte = sz - nSkip; aRes[i].idx = i; printf("/* Content of file %s */\n", aRes[i].zName); printf("static const unsigned char bidata%d[%d] = {\n ", i, sz+1-nSkip); for(j=nSkip, n=0; j<=sz; j++){ |
︙ | ︙ |
Changes to src/mkindex.c.
︙ | ︙ | |||
389 390 391 392 393 394 395 | /* ** Build the binary search table. */ void build_table(void){ int i; int nWeb = 0; int mxLen = 0; | < | 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | /* ** Build the binary search table. */ void build_table(void){ int i; int nWeb = 0; int mxLen = 0; qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare); printf( "/* Automatically generated code\n" "** DO NOT EDIT!\n" "**\n" |
︙ | ︙ |
Changes to src/piechart.c.
︙ | ︙ | |||
140 141 142 143 144 145 146 | r2 = cx<cy ? cx : cy; r = r2 - 80.0; if( r<0.33333*r2 ) r = 0.33333*r2; h = 0; zFg = skin_detail_boolean("white-foreground") ? "white" : "black"; db_prepare(&q, "SELECT sum(amt), count(*) FROM piechart"); | | > > > | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | r2 = cx<cy ? cx : cy; r = r2 - 80.0; if( r<0.33333*r2 ) r = 0.33333*r2; h = 0; zFg = skin_detail_boolean("white-foreground") ? "white" : "black"; db_prepare(&q, "SELECT sum(amt), count(*) FROM piechart"); if( db_step(&q)!=SQLITE_ROW ){ db_finalize(&q); return; } rTotal = db_column_double(&q, 0); nTotal = db_column_int(&q, 1); db_finalize(&q); rTooSmall = 0.0; nTooSmall = 0; if( (pieFlags & PIE_OTHER)!=0 && nTotal>1 ){ db_prepare(&q, "SELECT sum(amt), count(*) FROM piechart WHERE amt<:amt"); |
︙ | ︙ |
Changes to src/publish.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | ** "unpublished" commands. */ #include "config.h" #include "publish.h" #include <assert.h> /* | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ** "unpublished" commands. */ #include "config.h" #include "publish.h" #include <assert.h> /* ** COMMAND: unpublished* ** ** Usage: %fossil unpublished ?OPTIONS? ** ** Show a list of unpublished or "private" artifacts. Unpublished artifacts ** will never push and hence will not be shared with collaborators. ** ** By default, this command only shows unpublished check-ins. To show |
︙ | ︙ | |||
48 49 50 51 52 53 54 | "IN (SELECT rid FROM private CROSS JOIN event" " WHERE private.rid=event.objid" " AND event.type='ci')", 0); } } /* | | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | "IN (SELECT rid FROM private CROSS JOIN event" " WHERE private.rid=event.objid" " AND event.type='ci')", 0); } } /* ** COMMAND: publish* ** ** Usage: %fossil publish ?--only? TAGS... ** ** Cause artifacts identified by TAGS... to be published (made non-private). ** This can be used (for example) to convert a private branch into a public ** branch, or to publish a bundle that was imported privately. ** |
︙ | ︙ |
Changes to src/purge.c.
︙ | ︙ | |||
505 506 507 508 509 510 511 | ** ** TBD... ** ** COMMON OPTIONS: ** ** --explain Make no changes, but show what would happen. ** --dry-run An alias for --explain | < < < < < < < < < < < | 505 506 507 508 509 510 511 512 513 514 515 516 517 518 | ** ** TBD... ** ** COMMON OPTIONS: ** ** --explain Make no changes, but show what would happen. ** --dry-run An alias for --explain */ void purge_cmd(void){ int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY; const char *zSubcmd; int n; int i; Stmt q; |
︙ | ︙ |
Changes to src/rebuild.c.
︙ | ︙ | |||
599 600 601 602 603 604 605 | ** --noindex Always omit the full-text search index ** --pagesize N Set the database pagesize to N. (512..65536 and power of 2) ** --quiet Only show output if there are errors ** --randomize Scan artifacts in a random order ** --stats Show artifact statistics after rebuilding ** --vacuum Run VACUUM on the database after rebuilding ** --wal Set Write-Ahead-Log journalling mode on the database | < < | 599 600 601 602 603 604 605 606 607 608 609 610 611 612 | ** --noindex Always omit the full-text search index ** --pagesize N Set the database pagesize to N. (512..65536 and power of 2) ** --quiet Only show output if there are errors ** --randomize Scan artifacts in a random order ** --stats Show artifact statistics after rebuilding ** --vacuum Run VACUUM on the database after rebuilding ** --wal Set Write-Ahead-Log journalling mode on the database */ void rebuild_database(void){ int forceFlag; int randomizeFlag; int errCnt = 0; int omitVerify; int doClustering; |
︙ | ︙ | |||
1201 1202 1203 1204 1205 1206 1207 | ** Subdirectories are read, files with leading '.' in the filename are ignored. ** ** Options: ** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the ** file .rid in DIRECTORY. ** -P|--keep-private Mark the artifacts listed in the file .private in ** DIRECTORY as private in the new Fossil repository. | < < | 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 | ** Subdirectories are read, files with leading '.' in the filename are ignored. ** ** Options: ** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the ** file .rid in DIRECTORY. ** -P|--keep-private Mark the artifacts listed in the file .private in ** DIRECTORY as private in the new Fossil repository. */ void reconstruct_cmd(void) { char *zPassword; int fKeepPrivate; fKeepRid1 = find_option("keep-rid1","K",0)!=0; fKeepPrivate = find_option("keep-private","P",0)!=0; if( g.argc!=4 ){ |
︙ | ︙ | |||
1276 1277 1278 1279 1280 1281 1282 | ** the file .rid1 in the DESTINATION directory. ** -L|--prefixlength N Set the length of the names of the DESTINATION ** subdirectories to N. ** --private Include private artifacts. ** -P|--keep-private Save the list of private artifacts to the file ** .private in the DESTINATION directory (implies ** the --private option). | < < | 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 | ** the file .rid1 in the DESTINATION directory. ** -L|--prefixlength N Set the length of the names of the DESTINATION ** subdirectories to N. ** --private Include private artifacts. ** -P|--keep-private Save the list of private artifacts to the file ** .private in the DESTINATION directory (implies ** the --private option). */ void deconstruct_cmd(void){ const char *zPrefixOpt; Stmt s; int privateFlag; int fKeepPrivate; |
︙ | ︙ |
Changes to src/sbsdiff.js.
︙ | ︙ | |||
20 21 22 23 24 25 26 | e = e || event; var len = {37: -SCROLL_LEN, 39: SCROLL_LEN}[e.keyCode]; if( !len ) return; txtCols[0].scrollLeft += len; return false; }; } | | > > > > > > > > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | e = e || event; var len = {37: -SCROLL_LEN, 39: SCROLL_LEN}[e.keyCode]; if( !len ) return; txtCols[0].scrollLeft += len; return false; }; } var i, diffs = document.querySelectorAll('.sbsdiffcols') /* Maintenance reminder: using forEach() here breaks MSIE<=11, and we need to keep those browsers working on the /info page. */; for(i=0; i<diffs.length; i++){ initSbsDiff(diffs[i]); } if(window.fossil && fossil.page){ /* Here we can use forEach() because the pages which use fossil.pages only work in HTML5-compliant browsers. */ fossil.page.tweakSbsDiffs = function(){ document.querySelectorAll('.sbsdiffcols').forEach(initSbsDiff); }; } })(); |
Changes to src/scroll.js.
1 | /* Cause the page to scroll so that the #scrollToMe is visible */ | | | 1 2 | /* Cause the page to scroll so that the #scrollToMe is visible */ (document.getElementById('scrollToMe')||document.body).scrollIntoView(true); |
Changes to src/setup.c.
︙ | ︙ | |||
1075 1076 1077 1078 1079 1080 1081 | @ be allowed. For example, to allow unsafe HTML only for checked-in files, @ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except @ in forum posts, make this setting be "<b>btw</b>". The default is an @ empty string which means that Fossil never allows Markdown documents @ to generate unsafe HTML. @ (Property: "safe-html")</p> @ <hr /> | < < < < < < < < | < < | 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 | @ be allowed. For example, to allow unsafe HTML only for checked-in files, @ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except @ in forum posts, make this setting be "<b>btw</b>". The default is an @ empty string which means that Fossil never allows Markdown documents @ to generate unsafe HTML. @ (Property: "safe-html")</p> @ <hr /> onoff_attribute("Use HTML as wiki markup language", "wiki-use-html", "wiki-use-html", 0, 0); @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed @ but all other wiki formatting will be ignored.</p> @ <p><strong>CAUTION:</strong> when @ enabling, <i>all</i> HTML tags and attributes are accepted in the wiki. @ No sanitization is done. This means that it is very possible for malicious @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p> @ <p>This should <strong>only</strong> be enabled when wiki editing is limited @ to trusted users. It should <strong>not</strong> be used on a publicly @ editable wiki.</p> |
︙ | ︙ | |||
1233 1234 1235 1236 1237 1238 1239 | style_footer(); db_end_transaction(0); } /* ** WEBPAGE: setup_logo ** | | > > > > > > > | 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 | style_footer(); db_end_transaction(0); } /* ** WEBPAGE: setup_logo ** ** Administrative page for changing the logo, background, and icon images. */ void setup_logo(void){ const char *zLogoMtime = db_get_mtime("logo-image", 0, 0); const char *zLogoMime = db_get("logo-mimetype","image/gif"); const char *aLogoImg = P("logoim"); int szLogoImg = atoi(PD("logoim:bytes","0")); const char *zBgMtime = db_get_mtime("background-image", 0, 0); const char *zBgMime = db_get("background-mimetype","image/gif"); const char *aBgImg = P("bgim"); int szBgImg = atoi(PD("bgim:bytes","0")); const char *zIconMtime = db_get_mtime("icon-image", 0, 0); const char *zIconMime = db_get("icon-mimetype","image/gif"); const char *aIconImg = P("iconim"); int szIconImg = atoi(PD("iconim:bytes","0")); if( szLogoImg>0 ){ zLogoMime = PD("logoim:mimetype","image/gif"); } if( szBgImg>0 ){ zBgMime = PD("bgim:mimetype","image/gif"); } if( szIconImg>0 ){ zIconMime = PD("iconim:mimetype","image/gif"); } login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } db_begin_transaction(); if( !cgi_csrf_safe(1) ){ |
︙ | ︙ | |||
1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 | }else if( P("clrbg")!=0 ){ db_multi_exec( "DELETE FROM config WHERE name IN " "('background-image','background-mimetype')" ); db_end_transaction(0); cgi_redirect("setup_logo"); } style_header("Edit Project Logo And Background"); @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> @ and looks like this:</p> @ <blockquote><p><img src="%s(g.zTop)/logo/%z(zLogoMtime)" \ @ alt="logo" border="1" /> @ </p></blockquote> | > > > > > > > > > > > > > > > > > > > > > > > > > | 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 | }else if( P("clrbg")!=0 ){ db_multi_exec( "DELETE FROM config WHERE name IN " "('background-image','background-mimetype')" ); db_end_transaction(0); cgi_redirect("setup_logo"); }else if( P("seticon")!=0 && zIconMime && zIconMime[0] && szIconImg>0 ){ Blob img; Stmt ins; blob_init(&img, aIconImg, szIconImg); db_prepare(&ins, "REPLACE INTO config(name,value,mtime)" " VALUES('icon-image',:bytes,now())" ); db_bind_blob(&ins, ":bytes", &img); db_step(&ins); db_finalize(&ins); db_multi_exec( "REPLACE INTO config(name,value,mtime)" " VALUES('icon-mimetype',%Q,now())", zIconMime ); db_end_transaction(0); cgi_redirect("setup_logo"); }else if( P("clricon")!=0 ){ db_multi_exec( "DELETE FROM config WHERE name IN " "('icon-image','icon-mimetype')" ); db_end_transaction(0); cgi_redirect("setup_logo"); } style_header("Edit Project Logo And Background"); @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> @ and looks like this:</p> @ <blockquote><p><img src="%s(g.zTop)/logo/%z(zLogoMtime)" \ @ alt="logo" border="1" /> @ </p></blockquote> |
︙ | ︙ | |||
1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 | @ <input type="file" name="bgim" size="60" accept="image/*" /> @ <p align="center"> @ <input type="submit" name="setbg" value="Change Background" /> @ <input type="submit" name="clrbg" value="Revert To Default" /></p> @ </div></form> @ <p>(Properties: "background-image" and "background-mimetype") @ <hr /> @ @ <p><span class="note">Note:</span> Your browser has probably cached these @ images, so you may need to press the Reload button before changes will @ take effect. </p> style_footer(); db_end_transaction(0); } | > > > > > > > > > > > > > > > > > > > > > > > > | 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 | @ <input type="file" name="bgim" size="60" accept="image/*" /> @ <p align="center"> @ <input type="submit" name="setbg" value="Change Background" /> @ <input type="submit" name="clrbg" value="Revert To Default" /></p> @ </div></form> @ <p>(Properties: "background-image" and "background-mimetype") @ <hr /> @ @ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b> @ and looks like this:</p> @ <blockquote><p><img src="%s(g.zTop)/favicon.ico/%z(zIconMtime)" \ @ alt="icon" border=1 /> @ </p></blockquote> @ @ <form action="%s(g.zTop)/setup_logo" method="post" @ enctype="multipart/form-data"><div> @ <p>The icon image is accessible to all users at this URL: @ <a href="%s(g.zBaseURL)/favicon.ico">%s(g.zBaseURL)/favicon.ico</a>. @ The icon image may or may not appear on each @ page depending on the web browser in use and the MIME-Types that it @ supports for icon images. @ To change the icon image, use the following form:</p> login_insert_csrf_secret(); @ Icon image file: @ <input type="file" name="iconim" size="60" accept="image/*" /> @ <p align="center"> @ <input type="submit" name="seticon" value="Change Icon" /> @ <input type="submit" name="clricon" value="Revert To Default" /></p> @ </div></form> @ <p>(Properties: "icon-image" and "icon-mimetype") @ <hr /> @ @ <p><span class="note">Note:</span> Your browser has probably cached these @ images, so you may need to press the Reload button before changes will @ take effect. </p> style_footer(); db_end_transaction(0); } |
︙ | ︙ |
Changes to src/setupuser.c.
︙ | ︙ | |||
700 701 702 703 704 705 706 | } @ <input type="submit" name="can" value="Cancel"></td> @ </tr> } @ </table> @ </div></form> @ </div> | | | 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 | } @ <input type="submit" name="can" value="Cancel"></td> @ </tr> } @ </table> @ </div></form> @ </div> builtin_request_js("useredit.js"); @ <hr> @ <h1>Notes On Privileges And Capabilities:</h1> @ <ul> if( higherUser ){ @ <li><p class="missingPriv"> @ User %h(zLogin) has Setup privileges and you only have Admin privileges @ so you are not permitted to make changes to %h(zLogin). |
︙ | ︙ |
Changes to src/sha1.c.
︙ | ︙ | |||
505 506 507 508 509 510 511 512 513 514 515 516 517 518 | ** If a file is named "-" then take its content from standard input. ** Options: ** ** -h, --dereference If FILE is a symbolic link, compute the hash ** on the object that the link points to. Normally, ** the hash is over the name of the object that ** the link points to. */ void sha1sum_test(void){ int i; Blob in; Blob cksum; int eFType = SymFILE; if( find_option("dereference","h",0)!=0 ){ | > > | 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 | ** If a file is named "-" then take its content from standard input. ** Options: ** ** -h, --dereference If FILE is a symbolic link, compute the hash ** on the object that the link points to. Normally, ** the hash is over the name of the object that ** the link points to. ** ** See also: [[md5sum]], [[sha3sum]] */ void sha1sum_test(void){ int i; Blob in; Blob cksum; int eFType = SymFILE; if( find_option("dereference","h",0)!=0 ){ |
︙ | ︙ |
Changes to src/sha3.c.
︙ | ︙ | |||
635 636 637 638 639 640 641 642 643 644 645 646 647 648 | ** --256 Compute a SHA3-256 hash (the default) ** --384 Compute a SHA3-384 hash ** --512 Compute a SHA3-512 hash ** --size N An N-bit hash. N must be a multiple of 32 between ** 128 and 512. ** -h, --dereference If FILE is a symbolic link, compute the hash on ** the object pointed to, not on the link itself. */ void sha3sum_test(void){ int i; Blob in; Blob cksum; int iSize = 256; int eFType = SymFILE; | > > | 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 | ** --256 Compute a SHA3-256 hash (the default) ** --384 Compute a SHA3-384 hash ** --512 Compute a SHA3-512 hash ** --size N An N-bit hash. N must be a multiple of 32 between ** 128 and 512. ** -h, --dereference If FILE is a symbolic link, compute the hash on ** the object pointed to, not on the link itself. ** ** See also: [[md5sum]], [[sha1sum]] */ void sha3sum_test(void){ int i; Blob in; Blob cksum; int iSize = 256; int eFType = SymFILE; |
︙ | ︙ |
Changes to src/shell.c.
︙ | ︙ | |||
637 638 639 640 641 642 643 644 645 646 647 648 649 650 | int n = 0; while( *z ){ if( (0xc0&*(z++))!=0x80 ) n++; } return n; } /* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer ** to the text. NULL is returned at end of file, or if malloc() ** fails. ** ** If zLine is not NULL then it is a malloced buffer returned from | > > > > > > > > > > > > > > > | 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 | int n = 0; while( *z ){ if( (0xc0&*(z++))!=0x80 ) n++; } return n; } /* ** Return true if zFile does not exist or if it is not an ordinary file. */ #ifdef _WIN32 # define notNormalFile(X) 0 #else static int notNormalFile(const char *zFile){ struct stat x; int rc; memset(&x, 0, sizeof(x)); rc = stat(zFile, &x); return rc || !S_ISREG(x.st_mode); } #endif /* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer ** to the text. NULL is returned at end of file, or if malloc() ** fails. ** ** If zLine is not NULL then it is a malloced buffer returned from |
︙ | ︙ | |||
4043 4044 4045 4046 4047 4048 4049 | pSubVfs = ORIGVFS(pVfs); if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags); } p = (ApndFile*)pFile; memset(p, 0, sizeof(*p)); pSubFile = ORIGFILE(pFile); | | | 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 | pSubVfs = ORIGVFS(pVfs); if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags); } p = (ApndFile*)pFile; memset(p, 0, sizeof(*p)); pSubFile = ORIGFILE(pFile); pFile->pMethods = &apnd_io_methods; rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags); if( rc ) goto apnd_open_done; rc = pSubFile->pMethods->xFileSize(pSubFile, &sz); if( rc ){ pSubFile->pMethods->xClose(pSubFile); goto apnd_open_done; } |
︙ | ︙ | |||
4977 4978 4979 4980 4981 4982 4983 | #endif int sqlite3_decimal_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; | < > > | 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 | #endif int sqlite3_decimal_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; static const struct { const char *zFuncName; int nArg; void (*xFunc)(sqlite3_context*,int,sqlite3_value**); } aFunc[] = { { "decimal", 1, decimalFunc }, { "decimal_cmp", 2, decimalCmpFunc }, { "decimal_add", 2, decimalAddFunc }, { "decimal_sub", 2, decimalSubFunc }, { "decimal_mul", 2, decimalMulFunc }, }; unsigned int i; (void)pzErrMsg; /* Unused parameter */ SQLITE_EXTENSION_INIT2(pApi); for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){ rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg, SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, 0, aFunc[i].xFunc, 0, 0); } if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
12447 12448 12449 12450 12451 12452 12453 | p->iIndent = 0; } /* ** Disable and restore .wheretrace and .selecttrace settings. */ #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) | | | | | | 12463 12464 12465 12466 12467 12468 12469 12470 12471 12472 12473 12474 12475 12476 12477 12478 12479 12480 12481 12482 12483 12484 12485 12486 12487 12488 12489 12490 12491 12492 12493 12494 12495 12496 | p->iIndent = 0; } /* ** Disable and restore .wheretrace and .selecttrace settings. */ #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) extern unsigned int sqlite3_unsupported_selecttrace; static int savedSelectTrace; #endif #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) extern int sqlite3WhereTrace; static int savedWhereTrace; #endif static void disable_debug_trace_modes(void){ #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) savedSelectTrace = sqlite3_unsupported_selecttrace; sqlite3_unsupported_selecttrace = 0; #endif #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) savedWhereTrace = sqlite3WhereTrace; sqlite3WhereTrace = 0; #endif } static void restore_debug_trace_modes(void){ #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) sqlite3_unsupported_selecttrace = savedSelectTrace; #endif #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) sqlite3WhereTrace = savedWhereTrace; #endif } /* Create the TEMP table used to store parameter bindings */ |
︙ | ︙ | |||
12617 12618 12619 12620 12621 12622 12623 | ** first, in order to determine column widths, before providing ** any output. */ static void exec_prepared_stmt_columnar( ShellState *p, /* Pointer to ShellState */ sqlite3_stmt *pStmt /* Statment to run */ ){ | | | > | | < | < | > | > | > > > > > > > > | > > > > | 12633 12634 12635 12636 12637 12638 12639 12640 12641 12642 12643 12644 12645 12646 12647 12648 12649 12650 12651 12652 12653 12654 12655 12656 12657 12658 12659 12660 12661 12662 12663 12664 12665 12666 12667 12668 12669 12670 12671 12672 12673 12674 12675 12676 12677 12678 | ** first, in order to determine column widths, before providing ** any output. */ static void exec_prepared_stmt_columnar( ShellState *p, /* Pointer to ShellState */ sqlite3_stmt *pStmt /* Statment to run */ ){ sqlite3_int64 nRow = 0; int nColumn = 0; char **azData = 0; sqlite3_int64 nAlloc = 0; const char *z; int rc; sqlite3_int64 i, nData; int j, nTotal, w, n; const char *colSep = 0; const char *rowSep = 0; rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW ) return; nColumn = sqlite3_column_count(pStmt); nAlloc = nColumn*4; azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); if( azData==0 ) shell_out_of_memory(); for(i=0; i<nColumn; i++){ azData[i] = strdup(sqlite3_column_name(pStmt,i)); } do{ if( (nRow+2)*nColumn >= nAlloc ){ nAlloc *= 2; azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*)); if( azData==0 ) shell_out_of_memory(); } nRow++; for(i=0; i<nColumn; i++){ z = (const char*)sqlite3_column_text(pStmt,i); azData[nRow*nColumn + i] = z ? strdup(z) : 0; } }while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ); if( nColumn>p->nWidth ){ p->colWidth = realloc(p->colWidth, nColumn*2*sizeof(int)); if( p->colWidth==0 ) shell_out_of_memory(); for(i=p->nWidth; i<nColumn; i++) p->colWidth[i] = 0; p->nWidth = nColumn; p->actualWidth = &p->colWidth[nColumn]; } |
︙ | ︙ | |||
12745 12746 12747 12748 12749 12750 12751 | }else if( p->cMode==MODE_Box ){ print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14); } columnar_end: if( seenInterrupt ){ utf8_printf(p->out, "Interrupt\n"); } | > > | | 12774 12775 12776 12777 12778 12779 12780 12781 12782 12783 12784 12785 12786 12787 12788 12789 12790 | }else if( p->cMode==MODE_Box ){ print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14); } columnar_end: if( seenInterrupt ){ utf8_printf(p->out, "Interrupt\n"); } nData = (nRow+1)*nColumn; for(i=0; i<nData; i++) free(azData[i]); sqlite3_free(azData); } /* ** Run a prepared statement */ static void exec_prepared_stmt( ShellState *pArg, /* Pointer to ShellState */ |
︙ | ︙ | |||
18513 18514 18515 18516 18517 18518 18519 | FILE *inSaved = p->in; int savedLineno = p->lineno; if( nArg!=2 ){ raw_printf(stderr, "Usage: .read FILE\n"); rc = 1; goto meta_command_exit; } | > | | | 18544 18545 18546 18547 18548 18549 18550 18551 18552 18553 18554 18555 18556 18557 18558 18559 18560 | FILE *inSaved = p->in; int savedLineno = p->lineno; if( nArg!=2 ){ raw_printf(stderr, "Usage: .read FILE\n"); rc = 1; goto meta_command_exit; } if( notNormalFile(azArg[1]) || (p->in = fopen(azArg[1], "rb"))==0 ){ utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); rc = 1; }else{ rc = process_input(p); fclose(p->in); } p->in = inSaved; |
︙ | ︙ | |||
18720 18721 18722 18723 18724 18725 18726 | }else{ rc = 0; } }else #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ | | | 18752 18753 18754 18755 18756 18757 18758 18759 18760 18761 18762 18763 18764 18765 18766 | }else{ rc = 0; } }else #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ sqlite3_unsupported_selecttrace = nArg>=2 ? (int)integerValue(azArg[1]) : 0xffff; }else #endif #if defined(SQLITE_ENABLE_SESSION) if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){ OpenSession *pSession = &p->aSession[0]; char **azCmd = &azArg[1]; |
︙ | ︙ |
Changes to src/skins.c.
︙ | ︙ | |||
1099 1100 1101 1102 1103 1104 1105 | if( !g.perm.Admin ){ @ <p>Administrators can optionally save or restore legacy skins, and/or @ undo a prior publish. }else{ @ <p>Visit the <a href='%R/setup_skin_admin'>Skin Admin</a> page @ for cleanup and recovery actions. } | | | 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 | if( !g.perm.Admin ){ @ <p>Administrators can optionally save or restore legacy skins, and/or @ undo a prior publish. }else{ @ <p>Visit the <a href='%R/setup_skin_admin'>Skin Admin</a> page @ for cleanup and recovery actions. } builtin_request_js("skin.js"); style_footer(); } |
Changes to src/sqlcmd.c.
︙ | ︙ | |||
170 171 172 173 174 175 176 177 178 179 180 181 182 183 | add_content_sql_commands(db); db_add_aux_functions(db); re_add_sql_func(db); search_sql_setup(db); foci_register(db); deltafunc_init(db); helptext_vtab_register(db); g.repositoryOpen = 1; g.db = db; sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "repository"); db_maybe_set_encryption_key(db, g.zRepositoryName); if( g.zLocalDbName ){ char *zSql = sqlite3_mprintf("ATTACH %Q AS 'localdb' KEY ''", g.zLocalDbName); | > | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | add_content_sql_commands(db); db_add_aux_functions(db); re_add_sql_func(db); search_sql_setup(db); foci_register(db); deltafunc_init(db); helptext_vtab_register(db); builtin_vtab_register(db); g.repositoryOpen = 1; g.db = db; sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "repository"); db_maybe_set_encryption_key(db, g.zRepositoryName); if( g.zLocalDbName ){ char *zSql = sqlite3_mprintf("ATTACH %Q AS 'localdb' KEY ''", g.zLocalDbName); |
︙ | ︙ | |||
289 290 291 292 293 294 295 296 297 298 299 300 301 302 | ** -R REPOSITORY Use REPOSITORY as the repository database ** ** All of the standard sqlite3 command-line shell options should also ** work. ** ** The following SQL extensions are provided with this Fossil-enhanced ** version of the sqlite3 command-line shell: ** ** checkin_mtime(X,Y) Return the mtime for the file Y (a BLOB.RID) ** found in check-in X (another BLOB.RID value). ** ** compress(X) Compress text X with the same algorithm used ** to compress artifacts in the BLOB table. ** | > > > > | 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | ** -R REPOSITORY Use REPOSITORY as the repository database ** ** All of the standard sqlite3 command-line shell options should also ** work. ** ** The following SQL extensions are provided with this Fossil-enhanced ** version of the sqlite3 command-line shell: ** ** builtin A virtual table that contains one row for ** each datafile that is built into the Fossil ** binary. ** ** checkin_mtime(X,Y) Return the mtime for the file Y (a BLOB.RID) ** found in check-in X (another BLOB.RID value). ** ** compress(X) Compress text X with the same algorithm used ** to compress artifacts in the BLOB table. ** |
︙ | ︙ | |||
321 322 323 324 325 326 327 328 329 330 331 332 333 334 | ** delta D and returns rows for each element of ** that delta. ** ** files_of_checkin(X) A table-valued function that returns info on ** all files contained in check-in X. Example: ** ** SELECT * FROM files_of_checkin('trunk'); ** ** now() Return the number of seconds since 1970. ** ** obscure(T) Obfuscate the text password T so that its ** original value is not readily visible. Fossil ** uses this same algorithm when storing passwords ** of remote URLs. | > > > > | 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | ** delta D and returns rows for each element of ** that delta. ** ** files_of_checkin(X) A table-valued function that returns info on ** all files contained in check-in X. Example: ** ** SELECT * FROM files_of_checkin('trunk'); ** ** helptext A virtual table with one row for each command, ** webpage, and setting together with the built-in ** help text. ** ** now() Return the number of seconds since 1970. ** ** obscure(T) Obfuscate the text password T so that its ** original value is not readily visible. Fossil ** uses this same algorithm when storing passwords ** of remote URLs. |
︙ | ︙ |
Changes to src/sqlite3.c.
︙ | ︙ | |||
995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 | # define _GNU_SOURCE #endif #if defined(__OpenBSD__) && !defined(_BSD_SOURCE) # define _BSD_SOURCE #endif /* ** For MinGW, check to see if we can include the header file containing its ** version information, among other things. Normally, this internal MinGW ** header file would [only] be included automatically by other MinGW header ** files; however, the contained version information is now required by this ** header file to work around binary compatibility issues (see below) and ** this is the only known way to reliably obtain it. This entire #if block | > > > > > > > > > | 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 | # define _GNU_SOURCE #endif #if defined(__OpenBSD__) && !defined(_BSD_SOURCE) # define _BSD_SOURCE #endif /* ** Macro to disable warnings about missing "break" at the end of a "case". */ #if GCC_VERSION>=7000000 # define deliberate_fall_through __attribute__((fallthrough)); #else # define deliberate_fall_through #endif /* ** For MinGW, check to see if we can include the header file containing its ** version information, among other things. Normally, this internal MinGW ** header file would [only] be included automatically by other MinGW header ** files; however, the contained version information is now required by this ** header file to work around binary compatibility issues (see below) and ** this is the only known way to reliably obtain it. This entire #if block |
︙ | ︙ | |||
1160 1161 1162 1163 1164 1165 1166 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.33.0" #define SQLITE_VERSION_NUMBER 3033000 | | | 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.33.0" #define SQLITE_VERSION_NUMBER 3033000 #define SQLITE_SOURCE_ID "2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
︙ | ︙ | |||
14530 14531 14532 14533 14534 14535 14536 | # define SELECTTRACE_ENABLED 1 #else # define SELECTTRACE_ENABLED 0 #endif #if defined(SQLITE_ENABLE_SELECTTRACE) # define SELECTTRACE_ENABLED 1 # define SELECTTRACE(K,P,S,X) \ | | | 14539 14540 14541 14542 14543 14544 14545 14546 14547 14548 14549 14550 14551 14552 14553 | # define SELECTTRACE_ENABLED 1 #else # define SELECTTRACE_ENABLED 0 #endif #if defined(SQLITE_ENABLE_SELECTTRACE) # define SELECTTRACE_ENABLED 1 # define SELECTTRACE(K,P,S,X) \ if(sqlite3_unsupported_selecttrace&(K)) \ sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\ sqlite3DebugPrintf X #else # define SELECTTRACE(K,P,S,X) # define SELECTTRACE_ENABLED 0 #endif |
︙ | ︙ | |||
14593 14594 14595 14596 14597 14598 14599 | ** The following value as a destructor means to use sqlite3DbFree(). ** The sqlite3DbFree() routine requires two parameters instead of the ** one parameter that destructors normally want. So we have to introduce ** this magic value that the code knows to handle differently. Any ** pointer will work here as long as it is distinct from SQLITE_STATIC ** and SQLITE_TRANSIENT. */ | | | 14602 14603 14604 14605 14606 14607 14608 14609 14610 14611 14612 14613 14614 14615 14616 | ** The following value as a destructor means to use sqlite3DbFree(). ** The sqlite3DbFree() routine requires two parameters instead of the ** one parameter that destructors normally want. So we have to introduce ** this magic value that the code knows to handle differently. Any ** pointer will work here as long as it is distinct from SQLITE_STATIC ** and SQLITE_TRANSIENT. */ #define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3OomFault) /* ** When SQLITE_OMIT_WSD is defined, it means that the target platform does ** not support Writable Static Data (WSD) such as global and static variables. ** All variables must either be on the stack or dynamically allocated from ** the heap. When WSD is unsupported, the variable declarations scattered ** throughout the SQLite code must become constants instead. The SQLITE_WSD |
︙ | ︙ | |||
14733 14734 14735 14736 14737 14738 14739 14740 14741 14742 14743 14744 14745 14746 | typedef int VList; /* ** Defer sourcing vdbe.h and btree.h until after the "u8" and ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque ** pointer types (i.e. FuncDef) defined above. */ /************** Include btree.h in the middle of sqliteInt.h *****************/ /************** Begin file btree.h *******************************************/ /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 14742 14743 14744 14745 14746 14747 14748 14749 14750 14751 14752 14753 14754 14755 14756 14757 14758 14759 14760 14761 14762 14763 14764 14765 14766 14767 14768 14769 14770 14771 14772 14773 14774 14775 14776 14777 14778 14779 14780 14781 14782 14783 14784 14785 14786 14787 14788 14789 14790 14791 14792 14793 14794 14795 14796 14797 14798 14799 14800 14801 14802 14803 14804 14805 14806 14807 14808 14809 14810 14811 14812 14813 14814 14815 14816 14817 14818 14819 14820 14821 14822 14823 14824 14825 14826 14827 14828 14829 14830 14831 14832 14833 14834 14835 14836 14837 14838 14839 14840 14841 14842 14843 14844 14845 14846 14847 14848 14849 14850 14851 14852 14853 14854 14855 14856 14857 14858 14859 14860 14861 14862 14863 14864 14865 14866 14867 14868 14869 14870 14871 14872 14873 14874 14875 14876 14877 14878 14879 14880 14881 14882 14883 14884 14885 14886 14887 14888 14889 14890 14891 14892 14893 14894 14895 14896 14897 14898 14899 14900 14901 14902 14903 14904 14905 14906 14907 14908 14909 14910 14911 14912 14913 14914 14915 14916 14917 14918 14919 14920 14921 14922 14923 14924 14925 14926 14927 14928 14929 14930 14931 14932 14933 14934 14935 14936 14937 14938 14939 14940 14941 14942 14943 14944 14945 14946 14947 14948 14949 14950 14951 14952 14953 14954 14955 14956 14957 14958 14959 14960 14961 14962 14963 14964 14965 14966 14967 14968 14969 14970 14971 14972 14973 14974 14975 14976 14977 14978 14979 14980 14981 14982 14983 14984 14985 14986 14987 14988 14989 14990 14991 14992 14993 14994 14995 14996 14997 14998 14999 15000 15001 15002 | typedef int VList; /* ** Defer sourcing vdbe.h and btree.h until after the "u8" and ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque ** pointer types (i.e. FuncDef) defined above. */ /************** Include pager.h in the middle of sqliteInt.h *****************/ /************** Begin file pager.h *******************************************/ /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This header file defines the interface that the sqlite page cache ** subsystem. The page cache subsystem reads and writes a file a page ** at a time and provides a journal for rollback. */ #ifndef SQLITE_PAGER_H #define SQLITE_PAGER_H /* ** Default maximum size for persistent journal files. A negative ** value means no limit. This value may be overridden using the ** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit". */ #ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1 #endif /* ** The type used to represent a page number. The first page in a file ** is called page 1. 0 is used to represent "not a page". */ typedef u32 Pgno; /* ** Each open file is managed by a separate instance of the "Pager" structure. */ typedef struct Pager Pager; /* ** Handle type for pages. */ typedef struct PgHdr DbPage; /* ** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is ** reserved for working around a windows/posix incompatibility). It is ** used in the journal to signify that the remainder of the journal file ** is devoted to storing a super-journal name - there are no more pages to ** roll back. See comments for function writeSuperJournal() in pager.c ** for details. */ #define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) /* ** Allowed values for the flags parameter to sqlite3PagerOpen(). ** ** NOTE: These values must match the corresponding BTREE_ values in btree.h. */ #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ #define PAGER_MEMORY 0x0002 /* In-memory database */ /* ** Valid values for the second argument to sqlite3PagerLockingMode(). */ #define PAGER_LOCKINGMODE_QUERY -1 #define PAGER_LOCKINGMODE_NORMAL 0 #define PAGER_LOCKINGMODE_EXCLUSIVE 1 /* ** Numeric constants that encode the journalmode. ** ** The numeric values encoded here (other than PAGER_JOURNALMODE_QUERY) ** are exposed in the API via the "PRAGMA journal_mode" command and ** therefore cannot be changed without a compatibility break. */ #define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */ #define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ #define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ #define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ #define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ #define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ /* ** Flags that make up the mask passed to sqlite3PagerGet(). */ #define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */ #define PAGER_GET_READONLY 0x02 /* Read-only page is acceptable */ /* ** Flags for sqlite3PagerSetFlags() ** ** Value constraints (enforced via assert()): ** PAGER_FULLFSYNC == SQLITE_FullFSync ** PAGER_CKPT_FULLFSYNC == SQLITE_CkptFullFSync ** PAGER_CACHE_SPILL == SQLITE_CacheSpill */ #define PAGER_SYNCHRONOUS_OFF 0x01 /* PRAGMA synchronous=OFF */ #define PAGER_SYNCHRONOUS_NORMAL 0x02 /* PRAGMA synchronous=NORMAL */ #define PAGER_SYNCHRONOUS_FULL 0x03 /* PRAGMA synchronous=FULL */ #define PAGER_SYNCHRONOUS_EXTRA 0x04 /* PRAGMA synchronous=EXTRA */ #define PAGER_SYNCHRONOUS_MASK 0x07 /* Mask for four values above */ #define PAGER_FULLFSYNC 0x08 /* PRAGMA fullfsync=ON */ #define PAGER_CKPT_FULLFSYNC 0x10 /* PRAGMA checkpoint_fullfsync=ON */ #define PAGER_CACHESPILL 0x20 /* PRAGMA cache_spill=ON */ #define PAGER_FLAGS_MASK 0x38 /* All above except SYNCHRONOUS */ /* ** The remainder of this file contains the declarations of the functions ** that make up the Pager sub-system API. See source code comments for ** a detailed description of each routine. */ /* Open and close a Pager connection. */ SQLITE_PRIVATE int sqlite3PagerOpen( sqlite3_vfs*, Pager **ppPager, const char*, int, int, int, void(*)(DbPage*) ); SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3*); SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *); SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); SQLITE_PRIVATE Pgno sqlite3PagerMaxPageCount(Pager*, Pgno); SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager*, int); SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64); SQLITE_PRIVATE void sqlite3PagerShrink(Pager*); SQLITE_PRIVATE void sqlite3PagerSetFlags(Pager*,unsigned); SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int); SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*); SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*); SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64); SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*); SQLITE_PRIVATE int sqlite3PagerFlush(Pager*); /* Functions used to obtain and release page references. */ SQLITE_PRIVATE int sqlite3PagerGet(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); SQLITE_PRIVATE void sqlite3PagerRef(DbPage*); SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*); SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage*); SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage*); /* Operations on page references. */ SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*); SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*); SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*); SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); /* Functions used to manage pager transactions and savepoints. */ SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*); SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int); SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zSuper, int); SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*); SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zSuper); SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager); #ifndef SQLITE_OMIT_WAL SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*); SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*); # ifdef SQLITE_ENABLE_SNAPSHOT SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager*, sqlite3_snapshot **ppSnapshot); SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager*, sqlite3_snapshot *pSnapshot); SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot); SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager); # endif #endif #if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_ENABLE_SETLK_TIMEOUT) SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager*, int); SQLITE_PRIVATE void sqlite3PagerWalDb(Pager*, sqlite3*); #else # define sqlite3PagerWalWriteLock(y,z) SQLITE_OK # define sqlite3PagerWalDb(x,y) #endif #ifdef SQLITE_DIRECT_OVERFLOW_READ SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno); #endif #ifdef SQLITE_ENABLE_ZIPVFS SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager); #endif /* Functions used to query pager state and configuration. */ SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager*); #ifdef SQLITE_DEBUG SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); #endif SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager*, int); SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*); SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*); SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*); SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); /* Functions used to truncate the database file. */ SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16); /* Functions to support testing and debugging. */ #if !defined(NDEBUG) || defined(SQLITE_TEST) SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*); SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage*); #endif #ifdef SQLITE_TEST SQLITE_PRIVATE int *sqlite3PagerStats(Pager*); SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); void disable_simulated_io_errors(void); void enable_simulated_io_errors(void); #else # define disable_simulated_io_errors() # define enable_simulated_io_errors() #endif #endif /* SQLITE_PAGER_H */ /************** End of pager.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include btree.h in the middle of sqliteInt.h *****************/ /************** Begin file btree.h *******************************************/ /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: |
︙ | ︙ | |||
14808 14809 14810 14811 14812 14813 14814 | SQLITE_PRIVATE int sqlite3BtreeSetSpillSize(Btree*,int); #if SQLITE_MAX_MMAP_SIZE>0 SQLITE_PRIVATE int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64); #endif SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(Btree*,unsigned); SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); | | | | | 15064 15065 15066 15067 15068 15069 15070 15071 15072 15073 15074 15075 15076 15077 15078 15079 15080 15081 15082 15083 15084 15085 15086 15087 15088 15089 15090 15091 | SQLITE_PRIVATE int sqlite3BtreeSetSpillSize(Btree*,int); #if SQLITE_MAX_MMAP_SIZE>0 SQLITE_PRIVATE int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64); #endif SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(Btree*,unsigned); SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); SQLITE_PRIVATE Pgno sqlite3BtreeMaxPageCount(Btree*,Pgno); SQLITE_PRIVATE Pgno sqlite3BtreeLastPage(Btree*); SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree*); SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int,int*); SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char*); SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int); SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int,int); SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, Pgno*, int flags); SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*); SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*); SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*); SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree); #ifndef SQLITE_OMIT_SHARED_CACHE SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock); |
︙ | ︙ | |||
14962 14963 14964 14965 14966 14967 14968 | ** FORDELETE cursor may return a null row: 0x01 0x00. */ #define BTREE_WRCSR 0x00000004 /* read-write cursor */ #define BTREE_FORDELETE 0x00000008 /* Cursor is for seek/delete only */ SQLITE_PRIVATE int sqlite3BtreeCursor( Btree*, /* BTree containing table to open */ | | | 15218 15219 15220 15221 15222 15223 15224 15225 15226 15227 15228 15229 15230 15231 15232 | ** FORDELETE cursor may return a null row: 0x01 0x00. */ #define BTREE_WRCSR 0x00000004 /* read-write cursor */ #define BTREE_FORDELETE 0x00000008 /* Cursor is for seek/delete only */ SQLITE_PRIVATE int sqlite3BtreeCursor( Btree*, /* BTree containing table to open */ Pgno iTable, /* Index of root page */ int wrFlag, /* 1 for writing. 0 for read-only */ struct KeyInfo*, /* First argument to compare function */ BtCursor *pCursor /* Space to write cursor structure */ ); SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void); SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); |
︙ | ︙ | |||
15053 15054 15055 15056 15057 15058 15059 | SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor*); #endif SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*); SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*); SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*); | | | 15309 15310 15311 15312 15313 15314 15315 15316 15317 15318 15319 15320 15321 15322 15323 | SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor*); #endif SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*); SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*); SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*); SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,Pgno*aRoot,int nRoot,int,int*); SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor*); #ifndef SQLITE_OMIT_INCRBLOB SQLITE_PRIVATE int sqlite3BtreePayloadChecked(BtCursor*, u32 offset, u32 amt, void*); SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); SQLITE_PRIVATE void sqlite3BtreeIncrblobCursor(BtCursor *); |
︙ | ︙ | |||
15190 15191 15192 15193 15194 15195 15196 | double *pReal; /* Used when p4type is P4_REAL */ FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */ sqlite3_context *pCtx; /* Used when p4type is P4_FUNCCTX */ CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ Mem *pMem; /* Used when p4type is P4_MEM */ VTable *pVtab; /* Used when p4type is P4_VTAB */ KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ | | | 15446 15447 15448 15449 15450 15451 15452 15453 15454 15455 15456 15457 15458 15459 15460 | double *pReal; /* Used when p4type is P4_REAL */ FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */ sqlite3_context *pCtx; /* Used when p4type is P4_FUNCCTX */ CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ Mem *pMem; /* Used when p4type is P4_MEM */ VTable *pVtab; /* Used when p4type is P4_VTAB */ KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ u32 *ai; /* Used when p4type is P4_INTARRAY */ SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ Table *pTab; /* Used when p4type is P4_TABLE */ #ifdef SQLITE_ENABLE_CURSOR_HINTS Expr *pExpr; /* Used when p4type is P4_EXPR */ #endif int (*xAdvance)(BtCursor *, int); } p4; |
︙ | ︙ | |||
15754 15755 15756 15757 15758 15759 15760 | #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, VdbeOp*); #endif #endif /* SQLITE_VDBE_H */ /************** End of vdbe.h ************************************************/ | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 16010 16011 16012 16013 16014 16015 16016 16017 16018 16019 16020 16021 16022 16023 | #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, VdbeOp*); #endif #endif /* SQLITE_VDBE_H */ /************** End of vdbe.h ************************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include pcache.h in the middle of sqliteInt.h ****************/ /************** Begin file pcache.h ******************************************/ /* ** 2008 August 05 ** ** The author disclaims copyright to this source code. In place of |
︙ | ︙ | |||
16841 16842 16843 16844 16845 16846 16847 | int nextPagesize; /* Pagesize after VACUUM if >0 */ u32 magic; /* Magic number for detect library misuse */ int nChange; /* Value returned by sqlite3_changes() */ int nTotalChange; /* Value returned by sqlite3_total_changes() */ int aLimit[SQLITE_N_LIMIT]; /* Limits */ int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ struct sqlite3InitInfo { /* Information used during initialization */ | | > > | > | 16850 16851 16852 16853 16854 16855 16856 16857 16858 16859 16860 16861 16862 16863 16864 16865 16866 16867 16868 16869 16870 16871 16872 16873 16874 16875 16876 16877 16878 16879 16880 16881 16882 | int nextPagesize; /* Pagesize after VACUUM if >0 */ u32 magic; /* Magic number for detect library misuse */ int nChange; /* Value returned by sqlite3_changes() */ int nTotalChange; /* Value returned by sqlite3_total_changes() */ int aLimit[SQLITE_N_LIMIT]; /* Limits */ int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ struct sqlite3InitInfo { /* Information used during initialization */ Pgno newTnum; /* Rootpage of table being initialized */ u8 iDb; /* Which db file is being initialized */ u8 busy; /* TRUE if currently initializing */ unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ unsigned imposterTable : 1; /* Building an imposter table */ unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ char **azInit; /* "type", "name", and "tbl_name" columns */ } init; int nVdbeActive; /* Number of VDBEs currently running */ int nVdbeRead; /* Number of active VDBEs that read or write */ int nVdbeWrite; /* Number of active VDBEs that read and write */ int nVdbeExec; /* Number of nested calls to VdbeExec() */ int nVDestroy; /* Number of active OP_VDestroy operations */ int nExtension; /* Number of loaded extensions */ void **aExtension; /* Array of shared library handles */ union { void (*xLegacy)(void*,const char*); /* Legacy trace function */ int (*xV2)(u32,void*,void*,void*); /* V2 Trace function */ } trace; void *pTraceArg; /* Argument to the trace function */ #ifndef SQLITE_OMIT_DEPRECATED void (*xProfile)(void*,const char*,u64); /* Profiling function */ void *pProfileArg; /* Argument to profile function */ #endif void *pCommitArg; /* Argument to xCommitCallback() */ int (*xCommitCallback)(void*); /* Invoked at every commit. */ |
︙ | ︙ | |||
17480 17481 17482 17483 17484 17485 17486 | Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ ExprList *pCheck; /* All CHECK constraints */ /* ... also used as column name list in a VIEW */ | | | 17492 17493 17494 17495 17496 17497 17498 17499 17500 17501 17502 17503 17504 17505 17506 | Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ ExprList *pCheck; /* All CHECK constraints */ /* ... also used as column name list in a VIEW */ Pgno tnum; /* Root BTree page for this table */ u32 nTabRef; /* Number of pointers to this Table */ u32 tabFlags; /* Mask of TF_* values */ i16 iPKey; /* If not negative, use aCol[iPKey] as the rowid */ i16 nCol; /* Number of columns in this table */ i16 nNVCol; /* Number of columns that are not VIRTUAL */ LogEst nRowLogEst; /* Estimated rows in table - from sqlite_stat1 table */ LogEst szTabRow; /* Estimated size of each table row in bytes */ |
︙ | ︙ | |||
17773 17774 17775 17776 17777 17778 17779 | char *zColAff; /* String defining the affinity of each column */ Index *pNext; /* The next index associated with the same table */ Schema *pSchema; /* Schema containing this index */ u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ const char **azColl; /* Array of collation sequence names for index */ Expr *pPartIdxWhere; /* WHERE clause for partial indices */ ExprList *aColExpr; /* Column expressions */ | | | 17785 17786 17787 17788 17789 17790 17791 17792 17793 17794 17795 17796 17797 17798 17799 | char *zColAff; /* String defining the affinity of each column */ Index *pNext; /* The next index associated with the same table */ Schema *pSchema; /* Schema containing this index */ u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ const char **azColl; /* Array of collation sequence names for index */ Expr *pPartIdxWhere; /* WHERE clause for partial indices */ ExprList *aColExpr; /* Column expressions */ Pgno tnum; /* DB Page containing root of this index */ LogEst szIdxRow; /* Estimated average row size in bytes */ u16 nKeyCol; /* Number of columns forming the key */ u16 nColumn; /* Number of columns stored in the index */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */ unsigned bUnordered:1; /* Use this index for == or IN queries only */ unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */ |
︙ | ︙ | |||
17899 17900 17901 17902 17903 17904 17905 | int iDistinct; /* Ephemeral table used to enforce DISTINCT */ } *aFunc; int nFunc; /* Number of entries in aFunc[] */ u32 selId; /* Select to which this AggInfo belongs */ AggInfo *pNext; /* Next in list of them all */ }; | < < < < < | 17911 17912 17913 17914 17915 17916 17917 17918 17919 17920 17921 17922 17923 17924 | int iDistinct; /* Ephemeral table used to enforce DISTINCT */ } *aFunc; int nFunc; /* Number of entries in aFunc[] */ u32 selId; /* Select to which this AggInfo belongs */ AggInfo *pNext; /* Next in list of them all */ }; /* ** The datatype ynVar is a signed integer, either 16-bit or 32-bit. ** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater ** than 32767 we have to make it 32-bit. 16-bit is preferred because ** it uses less memory in the Expr object, which is a big memory user ** in systems with lots of prepared statements. And few applications ** need more than about 10 or 20 variables. But some extreme users want |
︙ | ︙ | |||
18742 18743 18744 18745 18746 18747 18748 | ** first field in the recursive region. ************************************************************************/ Token sLastToken; /* The last token parsed */ ynVar nVar; /* Number of '?' variables seen in the SQL so far */ u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */ u8 explain; /* True if the EXPLAIN flag is found on the query */ | < < | 18749 18750 18751 18752 18753 18754 18755 18756 18757 18758 18759 18760 18761 18762 18763 | ** first field in the recursive region. ************************************************************************/ Token sLastToken; /* The last token parsed */ ynVar nVar; /* Number of '?' variables seen in the SQL so far */ u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */ u8 explain; /* True if the EXPLAIN flag is found on the query */ u8 eParseMode; /* PARSE_MODE_XXX constant */ #ifndef SQLITE_OMIT_VIRTUALTABLE int nVtabLock; /* Number of virtual tables to lock */ #endif int nHeight; /* Expression tree height of current sub-select */ #ifndef SQLITE_OMIT_EXPLAIN int addrExplain; /* Address of current OP_Explain opcode */ #endif |
︙ | ︙ | |||
18988 18989 18990 18991 18992 18993 18994 18995 18996 18997 18998 18999 19000 19001 | typedef struct { sqlite3 *db; /* The database being initialized */ char **pzErrMsg; /* Error message stored here */ int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */ int rc; /* Result code stored here */ u32 mInitFlags; /* Flags controlling error messages */ u32 nInitRow; /* Number of rows processed */ } InitData; /* ** Allowed values for mInitFlags */ #define INITFLAG_AlterTable 0x0001 /* This is a reparse after ALTER TABLE */ | > | 18993 18994 18995 18996 18997 18998 18999 19000 19001 19002 19003 19004 19005 19006 19007 | typedef struct { sqlite3 *db; /* The database being initialized */ char **pzErrMsg; /* Error message stored here */ int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */ int rc; /* Result code stored here */ u32 mInitFlags; /* Flags controlling error messages */ u32 nInitRow; /* Number of rows processed */ Pgno mxPage; /* Maximum page number. 0 for no limit. */ } InitData; /* ** Allowed values for mInitFlags */ #define INITFLAG_AlterTable 0x0001 /* This is a reparse after ALTER TABLE */ |
︙ | ︙ | |||
19821 19822 19823 19824 19825 19826 19827 19828 19829 19830 19831 19832 19833 19834 19835 19836 | SQLITE_PRIVATE void sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*); SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); SQLITE_PRIVATE int sqlite3RealSameAsInt(double,sqlite3_int64); SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); SQLITE_PRIVATE int sqlite3Atoi(const char*); #ifndef SQLITE_OMIT_UTF16 SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); #endif SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); SQLITE_PRIVATE LogEst sqlite3LogEst(u64); | > > | 19827 19828 19829 19830 19831 19832 19833 19834 19835 19836 19837 19838 19839 19840 19841 19842 19843 19844 | SQLITE_PRIVATE void sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*); SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); SQLITE_PRIVATE int sqlite3RealSameAsInt(double,sqlite3_int64); SQLITE_PRIVATE void sqlite3Int64ToText(i64,char*); SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); SQLITE_PRIVATE int sqlite3Atoi(const char*); #ifndef SQLITE_OMIT_UTF16 SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); #endif SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); SQLITE_PRIVATE LogEst sqlite3LogEst(u64); |
︙ | ︙ | |||
19942 19943 19944 19945 19946 19947 19948 | #ifndef SQLITE_AMALGAMATION SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[]; SQLITE_PRIVATE const char sqlite3StrBINARY[]; SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[]; SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[]; SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config; SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions; | | | | 19950 19951 19952 19953 19954 19955 19956 19957 19958 19959 19960 19961 19962 19963 19964 19965 19966 19967 19968 19969 19970 19971 19972 | #ifndef SQLITE_AMALGAMATION SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[]; SQLITE_PRIVATE const char sqlite3StrBINARY[]; SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[]; SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[]; SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config; SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions; SQLITE_API extern u32 sqlite3_unsupported_selecttrace; #ifndef SQLITE_OMIT_WSD SQLITE_PRIVATE int sqlite3PendingByte; #endif #endif /* SQLITE_AMALGAMATION */ #ifdef VDBE_PROFILE SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt; #endif SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, Pgno, Pgno); SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*); SQLITE_PRIVATE void sqlite3AlterFunctions(void); SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*); SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int); |
︙ | ︙ | |||
20064 20065 20066 20067 20068 20069 20070 | #ifndef SQLITE_OMIT_LOAD_EXTENSION SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3*); #else # define sqlite3CloseExtensions(X) #endif #ifndef SQLITE_OMIT_SHARED_CACHE | | | 20072 20073 20074 20075 20076 20077 20078 20079 20080 20081 20082 20083 20084 20085 20086 | #ifndef SQLITE_OMIT_LOAD_EXTENSION SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3*); #else # define sqlite3CloseExtensions(X) #endif #ifndef SQLITE_OMIT_SHARED_CACHE SQLITE_PRIVATE void sqlite3TableLock(Parse *, int, Pgno, u8, const char *); #else #define sqlite3TableLock(v,w,x,y,z) #endif #ifdef SQLITE_TEST SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*); #endif |
︙ | ︙ | |||
20659 20660 20661 20662 20663 20664 20665 | #ifndef SQLITE_OMIT_WSD SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000; #endif /* ** Flags for select tracing and the ".selecttrace" macro of the CLI */ | | | 20667 20668 20669 20670 20671 20672 20673 20674 20675 20676 20677 20678 20679 20680 20681 | #ifndef SQLITE_OMIT_WSD SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000; #endif /* ** Flags for select tracing and the ".selecttrace" macro of the CLI */ SQLITE_API u32 sqlite3_unsupported_selecttrace = 0; /* #include "opcodes.h" */ /* ** Properties of opcodes. The OPFLG_INITIALIZER macro is ** created by mkopcodeh.awk during compilation. Data is obtained ** from the comments following the "case OP_xxxx:" statements in ** the vdbe.c file. |
︙ | ︙ | |||
20786 20787 20788 20789 20790 20791 20792 | #endif Bool isEphemeral:1; /* True for an ephemeral table */ Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ Bool seekHit:1; /* See the OP_SeekHit and OP_IfNoHope opcodes */ Btree *pBtx; /* Separate file holding temporary table */ i64 seqCount; /* Sequence counter */ | | | 20794 20795 20796 20797 20798 20799 20800 20801 20802 20803 20804 20805 20806 20807 20808 | #endif Bool isEphemeral:1; /* True for an ephemeral table */ Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ Bool seekHit:1; /* See the OP_SeekHit and OP_IfNoHope opcodes */ Btree *pBtx; /* Separate file holding temporary table */ i64 seqCount; /* Sequence counter */ u32 *aAltMap; /* Mapping from table to index column numbers */ /* Cached OP_Column parse information is only valid if cacheStatus matches ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of ** CACHE_STALE (0) and so setting cacheStatus=CACHE_STALE guarantees that ** the cache is out of date. */ u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ int seekResult; /* Result of previous sqlite3BtreeMoveto() or 0 |
︙ | ︙ | |||
21182 21183 21184 21185 21186 21187 21188 | /* ** Function prototypes */ SQLITE_PRIVATE void sqlite3VdbeError(Vdbe*, const char *, ...); SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); void sqliteVdbePopStack(Vdbe*,int); SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor*); | | | 21190 21191 21192 21193 21194 21195 21196 21197 21198 21199 21200 21201 21202 21203 21204 | /* ** Function prototypes */ SQLITE_PRIVATE void sqlite3VdbeError(Vdbe*, const char *, ...); SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); void sqliteVdbePopStack(Vdbe*,int); SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor*); SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, u32*); SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*); SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32); SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8); SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32); SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3*, AuxData**, int, int); |
︙ | ︙ | |||
21658 21659 21660 21661 21662 21663 21664 | /* ** Set *pCurrent to the total cache hits or misses encountered by all ** pagers the database handle is connected to. *pHighwater is always set ** to zero. */ case SQLITE_DBSTATUS_CACHE_SPILL: op = SQLITE_DBSTATUS_CACHE_WRITE+1; | | | 21666 21667 21668 21669 21670 21671 21672 21673 21674 21675 21676 21677 21678 21679 21680 | /* ** Set *pCurrent to the total cache hits or misses encountered by all ** pagers the database handle is connected to. *pHighwater is always set ** to zero. */ case SQLITE_DBSTATUS_CACHE_SPILL: op = SQLITE_DBSTATUS_CACHE_WRITE+1; /* no break */ deliberate_fall_through case SQLITE_DBSTATUS_CACHE_HIT: case SQLITE_DBSTATUS_CACHE_MISS: case SQLITE_DBSTATUS_CACHE_WRITE:{ int i; int nRet = 0; assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 ); assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 ); |
︙ | ︙ | |||
22814 22815 22816 22817 22818 22819 22820 | sqlite3_snprintf(20, &z[j],"%.16g",x.iJD/86400000.0); j+=sqlite3Strlen30(&z[j]); break; } case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break; case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break; case 's': { | < | > | 22822 22823 22824 22825 22826 22827 22828 22829 22830 22831 22832 22833 22834 22835 22836 22837 | sqlite3_snprintf(20, &z[j],"%.16g",x.iJD/86400000.0); j+=sqlite3Strlen30(&z[j]); break; } case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break; case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break; case 's': { i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); sqlite3Int64ToText(iS, &z[j]); j += sqlite3Strlen30(&z[j]); break; } case 'S': sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break; case 'w': { z[j++] = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; break; |
︙ | ︙ | |||
28535 28536 28537 28538 28539 28540 28541 | */ assert( width>=0 ); assert( precision>=(-1) ); switch( xtype ){ case etPOINTER: flag_long = sizeof(char*)==sizeof(i64) ? 2 : sizeof(char*)==sizeof(long int) ? 1 : 0; | | | | 28543 28544 28545 28546 28547 28548 28549 28550 28551 28552 28553 28554 28555 28556 28557 28558 28559 28560 28561 | */ assert( width>=0 ); assert( precision>=(-1) ); switch( xtype ){ case etPOINTER: flag_long = sizeof(char*)==sizeof(i64) ? 2 : sizeof(char*)==sizeof(long int) ? 1 : 0; /* no break */ deliberate_fall_through case etORDINAL: case etRADIX: cThousand = 0; /* no break */ deliberate_fall_through case etDECIMAL: if( infop->flags & FLAG_SIGNED ){ i64 v; if( bArgList ){ v = getIntArg(pArgList); }else if( flag_long ){ if( flag_long==2 ){ |
︙ | ︙ | |||
31768 31769 31770 31771 31772 31773 31774 31775 31776 31777 31778 31779 31780 31781 | #else return !sqlite3Atoi64(z, pResult, length, enc); #endif /* SQLITE_OMIT_FLOATING_POINT */ } #if defined(_MSC_VER) #pragma warning(default : 4756) #endif /* ** Compare the 19-character string zNum against the text representation ** value 2^63: 9223372036854775808. Return negative, zero, or positive ** if zNum is less than, equal to, or greater than the string. ** Note that zNum must contain exactly 19 characters. ** | > > > > > > > > > > > > > > > > > > > > > > > > | 31776 31777 31778 31779 31780 31781 31782 31783 31784 31785 31786 31787 31788 31789 31790 31791 31792 31793 31794 31795 31796 31797 31798 31799 31800 31801 31802 31803 31804 31805 31806 31807 31808 31809 31810 31811 31812 31813 | #else return !sqlite3Atoi64(z, pResult, length, enc); #endif /* SQLITE_OMIT_FLOATING_POINT */ } #if defined(_MSC_VER) #pragma warning(default : 4756) #endif /* ** Render an signed 64-bit integer as text. Store the result in zOut[]. ** ** The caller must ensure that zOut[] is at least 21 bytes in size. */ SQLITE_PRIVATE void sqlite3Int64ToText(i64 v, char *zOut){ int i; u64 x; char zTemp[22]; if( v<0 ){ x = (v==SMALLEST_INT64) ? ((u64)1)<<63 : (u64)-v; }else{ x = v; } i = sizeof(zTemp)-2; zTemp[sizeof(zTemp)-1] = 0; do{ zTemp[i--] = (x%10) + '0'; x = x/10; }while( x ); if( v<0 ) zTemp[i--] = '-'; memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i); } /* ** Compare the 19-character string zNum against the text representation ** value 2^63: 9223372036854775808. Return negative, zero, or positive ** if zNum is less than, equal to, or greater than the string. ** Note that zNum must contain exactly 19 characters. ** |
︙ | ︙ | |||
32009 32010 32011 32012 32013 32014 32015 | /* ** Return a 32-bit integer value extracted from a string. If the ** string is not an integer, just return 0. */ SQLITE_PRIVATE int sqlite3Atoi(const char *z){ int x = 0; | | > > > > > > > > > > > > > > > > > > | 32041 32042 32043 32044 32045 32046 32047 32048 32049 32050 32051 32052 32053 32054 32055 32056 32057 32058 32059 32060 32061 32062 32063 32064 32065 32066 32067 32068 32069 32070 32071 32072 32073 32074 32075 | /* ** Return a 32-bit integer value extracted from a string. If the ** string is not an integer, just return 0. */ SQLITE_PRIVATE int sqlite3Atoi(const char *z){ int x = 0; sqlite3GetInt32(z, &x); return x; } /* ** Try to convert z into an unsigned 32-bit integer. Return true on ** success and false if there is an error. ** ** Only decimal notation is accepted. */ SQLITE_PRIVATE int sqlite3GetUInt32(const char *z, u32 *pI){ u64 v = 0; int i; for(i=0; sqlite3Isdigit(z[i]); i++){ v = v*10 + z[i] - '0'; if( v>4294967296LL ){ *pI = 0; return 0; } } if( i==0 || z[i]!=0 ){ *pI = 0; return 0; } *pI = (u32)v; return 1; } /* ** The variable-length integer encoding is as follows: ** ** KEY: ** A = 0xxxxxxx 7 bits of data and one flag bit ** B = 1xxxxxxx 7 bits of data and one flag bit |
︙ | ︙ | |||
39186 39187 39188 39189 39190 39191 39192 | osUnlink(zFilename); pNew->ctrlFlags |= UNIXFILE_DELETE; } #endif if( rc!=SQLITE_OK ){ if( h>=0 ) robust_close(pNew, h, __LINE__); }else{ | | | 39236 39237 39238 39239 39240 39241 39242 39243 39244 39245 39246 39247 39248 39249 39250 | osUnlink(zFilename); pNew->ctrlFlags |= UNIXFILE_DELETE; } #endif if( rc!=SQLITE_OK ){ if( h>=0 ) robust_close(pNew, h, __LINE__); }else{ pId->pMethods = pLockingStyle; OpenCounter(+1); verifyDbFile(pNew); } return rc; } /* |
︙ | ︙ | |||
46897 46898 46899 46900 46901 46902 46903 | }else #endif { sqlite3_free(zConverted); } sqlite3_free(zTmpname); | | | 46947 46948 46949 46950 46951 46952 46953 46954 46955 46956 46957 46958 46959 46960 46961 | }else #endif { sqlite3_free(zConverted); } sqlite3_free(zTmpname); id->pMethods = pAppData ? pAppData->pMethod : &winIoMethod; pFile->pVfs = pVfs; pFile->h = h; if( isReadonly ){ pFile->ctrlFlags |= WINFILE_RDONLY; } if( (flags & SQLITE_OPEN_MAIN_DB) && sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) |
︙ | ︙ | |||
48123 48124 48125 48126 48127 48128 48129 | if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFile, flags, pOutFlags); } memset(p, 0, sizeof(*p)); p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ *pOutFlags = flags | SQLITE_OPEN_MEMORY; | | | 48173 48174 48175 48176 48177 48178 48179 48180 48181 48182 48183 48184 48185 48186 48187 | if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFile, flags, pOutFlags); } memset(p, 0, sizeof(*p)); p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ *pOutFlags = flags | SQLITE_OPEN_MEMORY; pFile->pMethods = &memdb_io_methods; p->szMax = sqlite3GlobalConfig.mxMemdbSize; return SQLITE_OK; } #if 0 /* Only used to delete rollback journals, super-journals, and WAL ** files, none of which exist in memdb. So this routine is never used */ /* |
︙ | ︙ | |||
52440 52441 52442 52443 52444 52445 52446 | */ #if SQLITE_MAX_MMAP_SIZE>0 # define USEFETCH(x) ((x)->bUseFetch) #else # define USEFETCH(x) 0 #endif | < < < < < | 52490 52491 52492 52493 52494 52495 52496 52497 52498 52499 52500 52501 52502 52503 | */ #if SQLITE_MAX_MMAP_SIZE>0 # define USEFETCH(x) ((x)->bUseFetch) #else # define USEFETCH(x) 0 #endif /* ** The argument to this macro is a file descriptor (type sqlite3_file*). ** Return 0 if it is not open, or non-zero (but not 1) if it is. ** ** This is so that expressions can be written as: ** ** if( isOpen(pPager->jfd) ){ ... |
︙ | ︙ | |||
54151 54152 54153 54154 54155 54156 54157 | char *zSuperPtr; /* Space to hold super-journal filename */ int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ /* Allocate space for both the pJournal and pSuper file descriptors. ** If successful, open the super-journal file for reading. */ pSuper = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); | < > > | 54196 54197 54198 54199 54200 54201 54202 54203 54204 54205 54206 54207 54208 54209 54210 54211 54212 54213 54214 54215 54216 | char *zSuperPtr; /* Space to hold super-journal filename */ int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ /* Allocate space for both the pJournal and pSuper file descriptors. ** If successful, open the super-journal file for reading. */ pSuper = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); if( !pSuper ){ rc = SQLITE_NOMEM_BKPT; pJournal = 0; }else{ const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_SUPER_JOURNAL); rc = sqlite3OsOpen(pVfs, zSuper, pSuper, flags, 0); pJournal = (sqlite3_file *)(((u8 *)pSuper) + pVfs->szOsFile); } if( rc!=SQLITE_OK ) goto delsuper_out; /* Load the entire super-journal file into space obtained from ** sqlite3_malloc() and pointed to by zSuperJournal. Also obtain ** sufficient space (in zSuperPtr) to hold the names of super-journal ** files extracted from regular rollback-journals. |
︙ | ︙ | |||
55417 55418 55419 55420 55421 55422 55423 | /* ** Attempt to set the maximum database page count if mxPage is positive. ** Make no changes if mxPage is zero or negative. And never reduce the ** maximum page count below the current size of the database. ** ** Regardless of mxPage, return the current maximum page count. */ | | | 55463 55464 55465 55466 55467 55468 55469 55470 55471 55472 55473 55474 55475 55476 55477 | /* ** Attempt to set the maximum database page count if mxPage is positive. ** Make no changes if mxPage is zero or negative. And never reduce the ** maximum page count below the current size of the database. ** ** Regardless of mxPage, return the current maximum page count. */ SQLITE_PRIVATE Pgno sqlite3PagerMaxPageCount(Pager *pPager, Pgno mxPage){ if( mxPage>0 ){ pPager->mxPgno = mxPage; } assert( pPager->eState!=PAGER_OPEN ); /* Called only by OP_MaxPgcnt */ /* assert( pPager->mxPgno>=pPager->dbSize ); */ /* OP_MaxPgcnt ensures that the parameter passed to this function is not ** less than the total number of valid pages in the database. But this |
︙ | ︙ | |||
57144 57145 57146 57147 57148 57149 57150 | assert( pPg->pgno==pgno ); assert( pPg->pPager==pPager || pPg->pPager==0 ); noContent = (flags & PAGER_GET_NOCONTENT)!=0; if( pPg->pPager && !noContent ){ /* In this case the pcache already contains an initialized copy of ** the page. Return without further ado. */ | | | | | 57190 57191 57192 57193 57194 57195 57196 57197 57198 57199 57200 57201 57202 57203 57204 57205 57206 57207 57208 57209 57210 57211 57212 57213 57214 57215 | assert( pPg->pgno==pgno ); assert( pPg->pPager==pPager || pPg->pPager==0 ); noContent = (flags & PAGER_GET_NOCONTENT)!=0; if( pPg->pPager && !noContent ){ /* In this case the pcache already contains an initialized copy of ** the page. Return without further ado. */ assert( pgno!=PAGER_MJ_PGNO(pPager) ); pPager->aStat[PAGER_STAT_HIT]++; return SQLITE_OK; }else{ /* The pager cache has created a new page. Its content needs to ** be initialized. But first some error checks: ** ** (*) obsolete. Was: maximum page number is 2^31 ** (2) Never try to fetch the locking page */ if( pgno==PAGER_MJ_PGNO(pPager) ){ rc = SQLITE_CORRUPT_BKPT; goto pager_acquire_err; } pPg->pPager = pPager; assert( !isOpen(pPager->fd) || !MEMDB ); |
︙ | ︙ | |||
59861 59862 59863 59864 59865 59866 59867 | ** walIteratorInit() - Create a new iterator, ** walIteratorNext() - Step an iterator, ** walIteratorFree() - Free an iterator. ** ** This functionality is used by the checkpoint code (see walCheckpoint()). */ struct WalIterator { | | | 59907 59908 59909 59910 59911 59912 59913 59914 59915 59916 59917 59918 59919 59920 59921 | ** walIteratorInit() - Create a new iterator, ** walIteratorNext() - Step an iterator, ** walIteratorFree() - Free an iterator. ** ** This functionality is used by the checkpoint code (see walCheckpoint()). */ struct WalIterator { u32 iPrior; /* Last result returned from the iterator */ int nSegment; /* Number of entries in aSegment[] */ struct WalSegment { int iNext; /* Next slot in aIndex[] not yet returned */ ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ u32 *aPgno; /* Array of page numbers. */ int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */ int iZero; /* Frame number associated with aPgno[0] */ |
︙ | ︙ | |||
59943 59944 59945 59946 59947 59948 59949 | if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT; }else{ rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] ); assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); | > > | | 59989 59990 59991 59992 59993 59994 59995 59996 59997 59998 59999 60000 60001 60002 60003 60004 60005 | if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT; }else{ rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] ); assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); if( rc==SQLITE_OK ){ if( iPage>0 && sqlite3FaultSim(600) ) rc = SQLITE_NOMEM; }else if( (rc&0xff)==SQLITE_READONLY ){ pWal->readOnly |= WAL_SHM_RDONLY; if( rc==SQLITE_READONLY ){ rc = SQLITE_OK; } } } |
︙ | ︙ | |||
60318 60319 60320 60321 60322 60323 60324 60325 60326 60327 60328 60329 60330 60331 | int iHash = (iFrame+HASHTABLE_NPAGE-HASHTABLE_NPAGE_ONE-1) / HASHTABLE_NPAGE; assert( (iHash==0 || iFrame>HASHTABLE_NPAGE_ONE) && (iHash>=1 || iFrame<=HASHTABLE_NPAGE_ONE) && (iHash<=1 || iFrame>(HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE)) && (iHash>=2 || iFrame<=HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE) && (iHash<=2 || iFrame>(HASHTABLE_NPAGE_ONE+2*HASHTABLE_NPAGE)) ); return iHash; } /* ** Return the page number associated with frame iFrame in this WAL. */ static u32 walFramePgno(Wal *pWal, u32 iFrame){ | > | 60366 60367 60368 60369 60370 60371 60372 60373 60374 60375 60376 60377 60378 60379 60380 | int iHash = (iFrame+HASHTABLE_NPAGE-HASHTABLE_NPAGE_ONE-1) / HASHTABLE_NPAGE; assert( (iHash==0 || iFrame>HASHTABLE_NPAGE_ONE) && (iHash>=1 || iFrame<=HASHTABLE_NPAGE_ONE) && (iHash<=1 || iFrame>(HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE)) && (iHash>=2 || iFrame<=HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE) && (iHash<=2 || iFrame>(HASHTABLE_NPAGE_ONE+2*HASHTABLE_NPAGE)) ); assert( iHash>=0 ); return iHash; } /* ** Return the page number associated with frame iFrame in this WAL. */ static u32 walFramePgno(Wal *pWal, u32 iFrame){ |
︙ | ︙ | |||
60514 60515 60516 60517 60518 60519 60520 | */ assert( pWal->ckptLock==1 || pWal->ckptLock==0 ); assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 ); assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE ); assert( pWal->writeLock ); iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); | < < < < < < > < < > > | 60563 60564 60565 60566 60567 60568 60569 60570 60571 60572 60573 60574 60575 60576 60577 60578 60579 60580 60581 60582 60583 60584 60585 60586 60587 60588 60589 60590 60591 60592 60593 60594 60595 60596 60597 60598 60599 60600 60601 | */ assert( pWal->ckptLock==1 || pWal->ckptLock==0 ); assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 ); assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE ); assert( pWal->writeLock ); iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); if( rc ){ return rc; } WALTRACE(("WAL%p: recovery begin...\n", pWal)); memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); rc = sqlite3OsFileSize(pWal->pWalFd, &nSize); if( rc!=SQLITE_OK ){ goto recovery_error; } if( nSize>WAL_HDRSIZE ){ u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */ u32 *aPrivate = 0; /* Heap copy of *-shm hash being populated */ u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */ int szFrame; /* Number of bytes in buffer aFrame[] */ u8 *aData; /* Pointer to data part of aFrame buffer */ int szPage; /* Page size according to the log */ u32 magic; /* Magic value read from WAL header */ u32 version; /* Magic value read from WAL header */ int isValid; /* True if this frame is valid */ u32 iPg; /* Current 32KB wal-index page */ u32 iLastFrame; /* Last frame in wal, based on nSize alone */ /* Read in the WAL header. */ rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); if( rc!=SQLITE_OK ){ goto recovery_error; } |
︙ | ︙ | |||
60590 60591 60592 60593 60594 60595 60596 | if( version!=WAL_MAX_VERSION ){ rc = SQLITE_CANTOPEN_BKPT; goto finished; } /* Malloc a buffer to read frames into. */ szFrame = szPage + WAL_FRAME_HDRSIZE; | | > | > > > > > > > > > | > > | | | < | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > | > > > > > > > > < | 60634 60635 60636 60637 60638 60639 60640 60641 60642 60643 60644 60645 60646 60647 60648 60649 60650 60651 60652 60653 60654 60655 60656 60657 60658 60659 60660 60661 60662 60663 60664 60665 60666 60667 60668 60669 60670 60671 60672 60673 60674 60675 60676 60677 60678 60679 60680 60681 60682 60683 60684 60685 60686 60687 60688 60689 60690 60691 60692 60693 60694 60695 60696 60697 60698 60699 60700 60701 60702 60703 60704 60705 60706 60707 60708 60709 60710 60711 60712 60713 60714 60715 60716 60717 60718 60719 60720 60721 60722 60723 60724 60725 60726 60727 60728 60729 60730 60731 60732 60733 60734 60735 60736 60737 60738 60739 60740 60741 60742 60743 60744 60745 60746 60747 60748 60749 60750 60751 60752 60753 60754 60755 60756 60757 60758 60759 60760 60761 60762 60763 60764 60765 60766 60767 60768 60769 60770 60771 60772 60773 60774 | if( version!=WAL_MAX_VERSION ){ rc = SQLITE_CANTOPEN_BKPT; goto finished; } /* Malloc a buffer to read frames into. */ szFrame = szPage + WAL_FRAME_HDRSIZE; aFrame = (u8 *)sqlite3_malloc64(szFrame + WALINDEX_PGSZ); if( !aFrame ){ rc = SQLITE_NOMEM_BKPT; goto recovery_error; } aData = &aFrame[WAL_FRAME_HDRSIZE]; aPrivate = (u32*)&aData[szPage]; /* Read all frames from the log file. */ iLastFrame = (nSize - WAL_HDRSIZE) / szFrame; for(iPg=0; iPg<=(u32)walFramePage(iLastFrame); iPg++){ u32 *aShare; u32 iFrame; /* Index of last frame read */ u32 iLast = MIN(iLastFrame, HASHTABLE_NPAGE_ONE+iPg*HASHTABLE_NPAGE); u32 iFirst = 1 + (iPg==0?0:HASHTABLE_NPAGE_ONE+(iPg-1)*HASHTABLE_NPAGE); u32 nHdr, nHdr32; rc = walIndexPage(pWal, iPg, (volatile u32**)&aShare); if( rc ) break; pWal->apWiData[iPg] = aPrivate; for(iFrame=iFirst; iFrame<=iLast; iFrame++){ i64 iOffset = walFrameOffset(iFrame, szPage); u32 pgno; /* Database page number for frame */ u32 nTruncate; /* dbsize field from frame header */ /* Read and decode the next log frame. */ rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); if( rc!=SQLITE_OK ) break; isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame); if( !isValid ) break; rc = walIndexAppend(pWal, iFrame, pgno); if( NEVER(rc!=SQLITE_OK) ) break; /* If nTruncate is non-zero, this is a commit record. */ if( nTruncate ){ pWal->hdr.mxFrame = iFrame; pWal->hdr.nPage = nTruncate; pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); testcase( szPage<=32768 ); testcase( szPage>=65536 ); aFrameCksum[0] = pWal->hdr.aFrameCksum[0]; aFrameCksum[1] = pWal->hdr.aFrameCksum[1]; } } pWal->apWiData[iPg] = aShare; nHdr = (iPg==0 ? WALINDEX_HDR_SIZE : 0); nHdr32 = nHdr / sizeof(u32); #ifndef SQLITE_SAFER_WALINDEX_RECOVERY /* Memcpy() should work fine here, on all reasonable implementations. ** Technically, memcpy() might change the destination to some ** intermediate value before setting to the final value, and that might ** cause a concurrent reader to malfunction. Memcpy() is allowed to ** do that, according to the spec, but no memcpy() implementation that ** we know of actually does that, which is why we say that memcpy() ** is safe for this. Memcpy() is certainly a lot faster. */ memcpy(&aShare[nHdr32], &aPrivate[nHdr32], WALINDEX_PGSZ-nHdr); #else /* In the event that some platform is found for which memcpy() ** changes the destination to some intermediate value before ** setting the final value, this alternative copy routine is ** provided. */ { int i; for(i=nHdr32; i<WALINDEX_PGSZ/sizeof(u32); i++){ if( aShare[i]!=aPrivate[i] ){ /* Atomic memory operations are not required here because if ** the value needs to be changed, that means it is not being ** accessed concurrently. */ aShare[i] = aPrivate[i]; } } } #endif if( iFrame<=iLast ) break; } sqlite3_free(aFrame); } finished: if( rc==SQLITE_OK ){ volatile WalCkptInfo *pInfo; int i; pWal->hdr.aFrameCksum[0] = aFrameCksum[0]; pWal->hdr.aFrameCksum[1] = aFrameCksum[1]; walIndexWriteHdr(pWal); /* Reset the checkpoint-header. This is safe because this thread is ** currently holding locks that exclude all other writers and ** checkpointers. Then set the values of read-mark slots 1 through N. */ pInfo = walCkptInfo(pWal); pInfo->nBackfill = 0; pInfo->nBackfillAttempted = pWal->hdr.mxFrame; pInfo->aReadMark[0] = 0; for(i=1; i<WAL_NREADER; i++){ rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); if( rc==SQLITE_OK ){ if( i==1 && pWal->hdr.mxFrame ){ pInfo->aReadMark[i] = pWal->hdr.mxFrame; }else{ pInfo->aReadMark[i] = READMARK_NOT_USED; } walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); }else if( rc!=SQLITE_BUSY ){ goto recovery_error; } } /* If more than one frame was recovered from the log file, report an ** event via sqlite3_log(). This is to help with identifying performance ** problems caused by applications routinely shutting down without ** checkpointing the log file. */ if( pWal->hdr.nPage ){ sqlite3_log(SQLITE_NOTICE_RECOVER_WAL, "recovered %d frames from WAL file %s", pWal->hdr.mxFrame, pWal->zWalName ); } } recovery_error: WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok")); walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); return rc; } /* ** Close an open wal-index. */ static void walIndexClose(Wal *pWal, int isDelete){ |
︙ | ︙ | |||
61310 61311 61312 61313 61314 61315 61316 | */ if( rc==SQLITE_OK ){ i64 nReq = ((i64)mxPage * szPage); i64 nSize; /* Current size of database file */ sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0); rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); if( rc==SQLITE_OK && nSize<nReq ){ | > > > > > > > | | | > | 61408 61409 61410 61411 61412 61413 61414 61415 61416 61417 61418 61419 61420 61421 61422 61423 61424 61425 61426 61427 61428 61429 61430 61431 61432 61433 | */ if( rc==SQLITE_OK ){ i64 nReq = ((i64)mxPage * szPage); i64 nSize; /* Current size of database file */ sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0); rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); if( rc==SQLITE_OK && nSize<nReq ){ if( (nSize+65536+(i64)pWal->hdr.mxFrame*szPage)<nReq ){ /* If the size of the final database is larger than the current ** database plus the amount of data in the wal file, plus the ** maximum size of the pending-byte page (65536 bytes), then ** must be corruption somewhere. */ rc = SQLITE_CORRUPT_BKPT; }else{ sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT,&nReq); } } } /* Iterate through the contents of the WAL, copying data to the db file */ while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ i64 iOffset; assert( walFramePgno(pWal, iFrame)==iDbpage ); if( AtomicLoad(&db->u1.isInterrupted) ){ rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; |
︙ | ︙ | |||
64046 64047 64048 64049 64050 64051 64052 | Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */ u8 *aPgRef; /* 1 bit per page in the db (see above) */ Pgno nPage; /* Number of pages in the database */ int mxErr; /* Stop accumulating errors when this reaches zero */ int nErr; /* Number of messages written to zErrMsg so far */ int bOomFault; /* A memory allocation error has occurred */ const char *zPfx; /* Error message prefix */ | > | | 64152 64153 64154 64155 64156 64157 64158 64159 64160 64161 64162 64163 64164 64165 64166 64167 | Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */ u8 *aPgRef; /* 1 bit per page in the db (see above) */ Pgno nPage; /* Number of pages in the database */ int mxErr; /* Stop accumulating errors when this reaches zero */ int nErr; /* Number of messages written to zErrMsg so far */ int bOomFault; /* A memory allocation error has occurred */ const char *zPfx; /* Error message prefix */ Pgno v1; /* Value for first %u substitution in zPfx */ int v2; /* Value for second %d substitution in zPfx */ StrAccum errMsg; /* Accumulate the error message text here */ u32 *heap; /* Min-heap used for analyzing cell coverage */ sqlite3 *db; /* Database connection running the check */ }; /* ** Routines to read or write a two- and four-byte big-endian integer values. |
︙ | ︙ | |||
66511 66512 66513 66514 66515 66516 66517 | } /* ** Return the size of the database file in pages. If there is any kind of ** error, return ((unsigned int)-1). */ static Pgno btreePagecount(BtShared *pBt){ | < | | | 66618 66619 66620 66621 66622 66623 66624 66625 66626 66627 66628 66629 66630 66631 66632 66633 66634 66635 66636 | } /* ** Return the size of the database file in pages. If there is any kind of ** error, return ((unsigned int)-1). */ static Pgno btreePagecount(BtShared *pBt){ return pBt->nPage; } SQLITE_PRIVATE Pgno sqlite3BtreeLastPage(Btree *p){ assert( sqlite3BtreeHoldsMutex(p) ); return btreePagecount(p->pBt); } /* ** Get a page from the pager and initialize it. ** ** If pCur!=0 then the page is being fetched as part of a moveToChild() ** call. Do additional sanity checking on the page in this case. |
︙ | ︙ | |||
67304 67305 67306 67307 67308 67309 67310 | /* ** Set the maximum page count for a database if mxPage is positive. ** No changes are made if mxPage is 0 or negative. ** Regardless of the value of mxPage, return the maximum page count. */ | | | | 67410 67411 67412 67413 67414 67415 67416 67417 67418 67419 67420 67421 67422 67423 67424 67425 | /* ** Set the maximum page count for a database if mxPage is positive. ** No changes are made if mxPage is 0 or negative. ** Regardless of the value of mxPage, return the maximum page count. */ SQLITE_PRIVATE Pgno sqlite3BtreeMaxPageCount(Btree *p, Pgno mxPage){ Pgno n; sqlite3BtreeEnter(p); n = sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage); sqlite3BtreeLeave(p); return n; } /* |
︙ | ︙ | |||
68744 68745 68746 68747 68748 68749 68750 | ** will not work correctly. ** ** It is assumed that the sqlite3BtreeCursorZero() has been called ** on pCur to initialize the memory space prior to invoking this routine. */ static int btreeCursor( Btree *p, /* The btree */ | | | 68850 68851 68852 68853 68854 68855 68856 68857 68858 68859 68860 68861 68862 68863 68864 | ** will not work correctly. ** ** It is assumed that the sqlite3BtreeCursorZero() has been called ** on pCur to initialize the memory space prior to invoking this routine. */ static int btreeCursor( Btree *p, /* The btree */ Pgno iTable, /* Root page of table to open */ int wrFlag, /* 1 to write. 0 read-only */ struct KeyInfo *pKeyInfo, /* First arg to comparison function */ BtCursor *pCur /* Space for new cursor */ ){ BtShared *pBt = p->pBt; /* Shared b-tree handle */ BtCursor *pX; /* Looping over other all cursors */ |
︙ | ︙ | |||
68787 68788 68789 68790 68791 68792 68793 | assert( wrFlag==0 ); iTable = 0; } } /* Now that no other errors can occur, finish filling in the BtCursor ** variables and link the cursor into the BtShared list. */ | | | | | | 68893 68894 68895 68896 68897 68898 68899 68900 68901 68902 68903 68904 68905 68906 68907 68908 68909 68910 68911 68912 68913 68914 68915 68916 68917 68918 68919 68920 68921 68922 68923 68924 68925 68926 68927 68928 68929 68930 68931 68932 68933 68934 68935 68936 68937 68938 68939 68940 68941 68942 | assert( wrFlag==0 ); iTable = 0; } } /* Now that no other errors can occur, finish filling in the BtCursor ** variables and link the cursor into the BtShared list. */ pCur->pgnoRoot = iTable; pCur->iPage = -1; pCur->pKeyInfo = pKeyInfo; pCur->pBtree = p; pCur->pBt = pBt; pCur->curFlags = wrFlag ? BTCF_WriteFlag : 0; pCur->curPagerFlags = wrFlag ? 0 : PAGER_GET_READONLY; /* If there are two or more cursors on the same btree, then all such ** cursors *must* have the BTCF_Multiple flag set. */ for(pX=pBt->pCursor; pX; pX=pX->pNext){ if( pX->pgnoRoot==iTable ){ pX->curFlags |= BTCF_Multiple; pCur->curFlags |= BTCF_Multiple; } } pCur->pNext = pBt->pCursor; pBt->pCursor = pCur; pCur->eState = CURSOR_INVALID; return SQLITE_OK; } static int btreeCursorWithLock( Btree *p, /* The btree */ Pgno iTable, /* Root page of table to open */ int wrFlag, /* 1 to write. 0 read-only */ struct KeyInfo *pKeyInfo, /* First arg to comparison function */ BtCursor *pCur /* Space for new cursor */ ){ int rc; sqlite3BtreeEnter(p); rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur); sqlite3BtreeLeave(p); return rc; } SQLITE_PRIVATE int sqlite3BtreeCursor( Btree *p, /* The btree */ Pgno iTable, /* Root page of table to open */ int wrFlag, /* 1 to write. 0 read-only */ struct KeyInfo *pKeyInfo, /* First arg to xCompare() */ BtCursor *pCur /* Write new cursor here */ ){ if( p->sharable ){ return btreeCursorWithLock(p, iTable, wrFlag, pKeyInfo, pCur); }else{ |
︙ | ︙ | |||
69247 69248 69249 69250 69251 69252 69253 69254 69255 69256 69257 69258 69259 69260 | offset = (offset%ovflSize); } } assert( rc==SQLITE_OK && amt>0 ); while( nextPage ){ /* If required, populate the overflow page-list cache. */ assert( pCur->aOverflow[iIdx]==0 || pCur->aOverflow[iIdx]==nextPage || CORRUPT_DB ); pCur->aOverflow[iIdx] = nextPage; if( offset>=ovflSize ){ /* The only reason to read this page is to obtain the page | > | 69353 69354 69355 69356 69357 69358 69359 69360 69361 69362 69363 69364 69365 69366 69367 | offset = (offset%ovflSize); } } assert( rc==SQLITE_OK && amt>0 ); while( nextPage ){ /* If required, populate the overflow page-list cache. */ if( nextPage > pBt->nPage ) return SQLITE_CORRUPT_BKPT; assert( pCur->aOverflow[iIdx]==0 || pCur->aOverflow[iIdx]==nextPage || CORRUPT_DB ); pCur->aOverflow[iIdx] = nextPage; if( offset>=ovflSize ){ /* The only reason to read this page is to obtain the page |
︙ | ︙ | |||
70662 70663 70664 70665 70666 70667 70668 70669 70670 70671 70672 70673 70674 70675 | ** first trunk page in the current free-list. This block tests if it ** is possible to add the page as a new free-list leaf. */ if( nFree!=0 ){ u32 nLeaf; /* Initial number of leaf cells on trunk page */ iTrunk = get4byte(&pPage1->aData[32]); rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); if( rc!=SQLITE_OK ){ goto freepage_out; } nLeaf = get4byte(&pTrunk->aData[4]); assert( pBt->usableSize>32 ); | > > > > | 70769 70770 70771 70772 70773 70774 70775 70776 70777 70778 70779 70780 70781 70782 70783 70784 70785 70786 | ** first trunk page in the current free-list. This block tests if it ** is possible to add the page as a new free-list leaf. */ if( nFree!=0 ){ u32 nLeaf; /* Initial number of leaf cells on trunk page */ iTrunk = get4byte(&pPage1->aData[32]); if( iTrunk>btreePagecount(pBt) ){ rc = SQLITE_CORRUPT_BKPT; goto freepage_out; } rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); if( rc!=SQLITE_OK ){ goto freepage_out; } nLeaf = get4byte(&pTrunk->aData[4]); assert( pBt->usableSize>32 ); |
︙ | ︙ | |||
73466 73467 73468 73469 73470 73471 73472 | ** The type of type is determined by the flags parameter. Only the ** following values of flags are currently in use. Other values for ** flags might not work: ** ** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys ** BTREE_ZERODATA Used for SQL indices */ | | | 73577 73578 73579 73580 73581 73582 73583 73584 73585 73586 73587 73588 73589 73590 73591 | ** The type of type is determined by the flags parameter. Only the ** following values of flags are currently in use. Other values for ** flags might not work: ** ** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys ** BTREE_ZERODATA Used for SQL indices */ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ BtShared *pBt = p->pBt; MemPage *pRoot; Pgno pgnoRoot; int rc; int ptfFlags; /* Page-type flage for the root page of new table */ assert( sqlite3BtreeHoldsMutex(p) ); |
︙ | ︙ | |||
73499 73500 73501 73502 73503 73504 73505 73506 73507 73508 73509 73510 73511 73512 73513 73514 | invalidateAllOverflowCache(pBt); /* Read the value of meta[3] from the database to determine where the ** root page of the new table should go. meta[3] is the largest root-page ** created so far, so the new root-page is (meta[3]+1). */ sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); pgnoRoot++; /* The new root-page may not be allocated on a pointer-map page, or the ** PENDING_BYTE page. */ while( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) || pgnoRoot==PENDING_BYTE_PAGE(pBt) ){ pgnoRoot++; } | > > > | < | 73610 73611 73612 73613 73614 73615 73616 73617 73618 73619 73620 73621 73622 73623 73624 73625 73626 73627 73628 73629 73630 73631 73632 73633 73634 73635 73636 | invalidateAllOverflowCache(pBt); /* Read the value of meta[3] from the database to determine where the ** root page of the new table should go. meta[3] is the largest root-page ** created so far, so the new root-page is (meta[3]+1). */ sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); if( pgnoRoot>btreePagecount(pBt) ){ return SQLITE_CORRUPT_BKPT; } pgnoRoot++; /* The new root-page may not be allocated on a pointer-map page, or the ** PENDING_BYTE page. */ while( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) || pgnoRoot==PENDING_BYTE_PAGE(pBt) ){ pgnoRoot++; } assert( pgnoRoot>=3 ); /* Allocate a page. The page that currently resides at pgnoRoot will ** be moved to the allocated page (unless the allocated page happens ** to reside at pgnoRoot). */ rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, BTALLOC_EXACT); if( rc!=SQLITE_OK ){ |
︙ | ︙ | |||
73606 73607 73608 73609 73610 73611 73612 | ptfFlags = PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF; }else{ ptfFlags = PTF_ZERODATA | PTF_LEAF; } zeroPage(pRoot, ptfFlags); sqlite3PagerUnref(pRoot->pDbPage); assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 ); | | | | 73719 73720 73721 73722 73723 73724 73725 73726 73727 73728 73729 73730 73731 73732 73733 73734 73735 73736 | ptfFlags = PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF; }else{ ptfFlags = PTF_ZERODATA | PTF_LEAF; } zeroPage(pRoot, ptfFlags); sqlite3PagerUnref(pRoot->pDbPage); assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 ); *piTable = pgnoRoot; return SQLITE_OK; } SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree *p, Pgno *piTable, int flags){ int rc; sqlite3BtreeEnter(p); rc = btreeCreateTable(p, piTable, flags); sqlite3BtreeLeave(p); return rc; } |
︙ | ︙ | |||
74093 74094 74095 74096 74097 74098 74099 | /* ** Check the integrity of the freelist or of an overflow page list. ** Verify that the number of pages on the list is N. */ static void checkList( IntegrityCk *pCheck, /* Integrity checking context */ int isFreeList, /* True for a freelist. False for overflow page list */ | | | 74206 74207 74208 74209 74210 74211 74212 74213 74214 74215 74216 74217 74218 74219 74220 | /* ** Check the integrity of the freelist or of an overflow page list. ** Verify that the number of pages on the list is N. */ static void checkList( IntegrityCk *pCheck, /* Integrity checking context */ int isFreeList, /* True for a freelist. False for overflow page list */ Pgno iPage, /* Page number for first page in the list */ u32 N /* Expected number of pages in the list */ ){ int i; u32 expected = N; int nErrAtStart = pCheck->nErr; while( iPage!=0 && pCheck->mxErr ){ DbPage *pOvflPage; |
︙ | ︙ | |||
74225 74226 74227 74228 74229 74230 74231 | ** 2. Make sure integer cell keys are in order. ** 3. Check the integrity of overflow pages. ** 4. Recursively call checkTreePage on all children. ** 5. Verify that the depth of all children is the same. */ static int checkTreePage( IntegrityCk *pCheck, /* Context for the sanity check */ | | | 74338 74339 74340 74341 74342 74343 74344 74345 74346 74347 74348 74349 74350 74351 74352 | ** 2. Make sure integer cell keys are in order. ** 3. Check the integrity of overflow pages. ** 4. Recursively call checkTreePage on all children. ** 5. Verify that the depth of all children is the same. */ static int checkTreePage( IntegrityCk *pCheck, /* Context for the sanity check */ Pgno iPage, /* Page number of the page to check */ i64 *piMinKey, /* Write minimum integer primary key here */ i64 maxKey /* Error if integer primary key greater than this */ ){ MemPage *pPage = 0; /* The page being analyzed */ int i; /* Loop counter */ int rc; /* Result code from subroutine call */ int depth = -1, d2; /* Depth of a subtree */ |
︙ | ︙ | |||
74261 74262 74263 74264 74265 74266 74267 | /* Check that the page exists */ pBt = pCheck->pBt; usableSize = pBt->usableSize; if( iPage==0 ) return 0; if( checkRef(pCheck, iPage) ) return 0; | | | | 74374 74375 74376 74377 74378 74379 74380 74381 74382 74383 74384 74385 74386 74387 74388 74389 74390 | /* Check that the page exists */ pBt = pCheck->pBt; usableSize = pBt->usableSize; if( iPage==0 ) return 0; if( checkRef(pCheck, iPage) ) return 0; pCheck->zPfx = "Page %u: "; pCheck->v1 = iPage; if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){ checkAppendMsg(pCheck, "unable to get the page. error code=%d", rc); goto end_of_check; } /* Clear MemPage.isInit to make sure the corruption detection code in ** btreeInitPage() is executed. */ |
︙ | ︙ | |||
74288 74289 74290 74291 74292 74293 74294 | checkAppendMsg(pCheck, "free space corruption", rc); goto end_of_check; } data = pPage->aData; hdr = pPage->hdrOffset; /* Set up for cell analysis */ | | | | 74401 74402 74403 74404 74405 74406 74407 74408 74409 74410 74411 74412 74413 74414 74415 74416 74417 74418 74419 74420 74421 74422 74423 74424 74425 74426 74427 74428 74429 74430 74431 74432 74433 74434 74435 | checkAppendMsg(pCheck, "free space corruption", rc); goto end_of_check; } data = pPage->aData; hdr = pPage->hdrOffset; /* Set up for cell analysis */ pCheck->zPfx = "On tree page %u cell %d: "; contentOffset = get2byteNotZero(&data[hdr+5]); assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the ** number of cells on the page. */ nCell = get2byte(&data[hdr+3]); assert( pPage->nCell==nCell ); /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page ** immediately follows the b-tree page header. */ cellStart = hdr + 12 - 4*pPage->leaf; assert( pPage->aCellIdx==&data[cellStart] ); pCellIdx = &data[cellStart + 2*(nCell-1)]; if( !pPage->leaf ){ /* Analyze the right-child page of internal pages */ pgno = get4byte(&data[hdr+8]); #ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum ){ pCheck->zPfx = "On page %u at right child: "; checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage); } #endif depth = checkTreePage(pCheck, pgno, &maxKey, maxKey); keyCanBeEqual = 0; }else{ /* For leaf pages, the coverage check will occur in the same loop |
︙ | ︙ | |||
74449 74450 74451 74452 74453 74454 74455 | ** that gap is added to the fragmentation count. */ nFrag = 0; prev = contentOffset - 1; /* Implied first min-heap entry */ while( btreeHeapPull(heap,&x) ){ if( (prev&0xffff)>=(x>>16) ){ checkAppendMsg(pCheck, | | | | 74562 74563 74564 74565 74566 74567 74568 74569 74570 74571 74572 74573 74574 74575 74576 74577 74578 74579 74580 74581 74582 74583 74584 74585 74586 74587 74588 74589 74590 74591 | ** that gap is added to the fragmentation count. */ nFrag = 0; prev = contentOffset - 1; /* Implied first min-heap entry */ while( btreeHeapPull(heap,&x) ){ if( (prev&0xffff)>=(x>>16) ){ checkAppendMsg(pCheck, "Multiple uses for byte %u of page %u", x>>16, iPage); break; }else{ nFrag += (x>>16) - (prev&0xffff) - 1; prev = x; } } nFrag += usableSize - (prev&0xffff) - 1; /* EVIDENCE-OF: R-43263-13491 The total number of bytes in all fragments ** is stored in the fifth field of the b-tree page header. ** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the ** number of fragmented free bytes within the cell content area. */ if( heap[0]==0 && nFrag!=data[hdr+7] ){ checkAppendMsg(pCheck, "Fragmentation of %d bytes reported as %d on page %u", nFrag, data[hdr+7], iPage); } } end_of_check: if( !doCoverageCheck ) pPage->isInit = savedIsInit; releasePage(pPage); |
︙ | ︙ | |||
74492 74493 74494 74495 74496 74497 74498 74499 74500 74501 74502 | ** A read-only or read-write transaction must be opened before calling ** this function. ** ** Write the number of error seen in *pnErr. Except for some memory ** allocation errors, an error message held in memory obtained from ** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is ** returned. If a memory allocation error occurs, NULL is returned. */ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( sqlite3 *db, /* Database connection that is running the check */ Btree *p, /* The btree to be checked */ | > > > > > > > > > | > > > > > > > > > > | 74605 74606 74607 74608 74609 74610 74611 74612 74613 74614 74615 74616 74617 74618 74619 74620 74621 74622 74623 74624 74625 74626 74627 74628 74629 74630 74631 74632 74633 74634 74635 74636 74637 74638 74639 74640 74641 74642 74643 74644 74645 74646 74647 74648 74649 74650 74651 74652 | ** A read-only or read-write transaction must be opened before calling ** this function. ** ** Write the number of error seen in *pnErr. Except for some memory ** allocation errors, an error message held in memory obtained from ** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is ** returned. If a memory allocation error occurs, NULL is returned. ** ** If the first entry in aRoot[] is 0, that indicates that the list of ** root pages is incomplete. This is a "partial integrity-check". This ** happens when performing an integrity check on a single table. The ** zero is skipped, of course. But in addition, the freelist checks ** and the checks to make sure every page is referenced are also skipped, ** since obviously it is not possible to know which pages are covered by ** the unverified btrees. Except, if aRoot[1] is 1, then the freelist ** checks are still performed. */ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( sqlite3 *db, /* Database connection that is running the check */ Btree *p, /* The btree to be checked */ Pgno *aRoot, /* An array of root pages numbers for individual trees */ int nRoot, /* Number of entries in aRoot[] */ int mxErr, /* Stop reporting errors after this many */ int *pnErr /* Write number of errors seen to this variable */ ){ Pgno i; IntegrityCk sCheck; BtShared *pBt = p->pBt; u64 savedDbFlags = pBt->db->flags; char zErr[100]; int bPartial = 0; /* True if not checking all btrees */ int bCkFreelist = 1; /* True to scan the freelist */ VVA_ONLY( int nRef ); assert( nRoot>0 ); /* aRoot[0]==0 means this is a partial check */ if( aRoot[0]==0 ){ assert( nRoot>1 ); bPartial = 1; if( aRoot[1]!=1 ) bCkFreelist = 0; } sqlite3BtreeEnter(p); assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) ); assert( nRef>=0 ); sCheck.db = db; sCheck.pBt = pBt; |
︙ | ︙ | |||
74546 74547 74548 74549 74550 74551 74552 | } i = PENDING_BYTE_PAGE(pBt); if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); /* Check the integrity of the freelist */ | > | | | | | > > | | | | | | | | | | | | | | | > | > | | | | | | | | | | | | | | | > | 74678 74679 74680 74681 74682 74683 74684 74685 74686 74687 74688 74689 74690 74691 74692 74693 74694 74695 74696 74697 74698 74699 74700 74701 74702 74703 74704 74705 74706 74707 74708 74709 74710 74711 74712 74713 74714 74715 74716 74717 74718 74719 74720 74721 74722 74723 74724 74725 74726 74727 74728 74729 74730 74731 74732 74733 74734 74735 74736 74737 74738 74739 74740 74741 74742 74743 74744 74745 74746 74747 74748 74749 74750 74751 74752 74753 74754 74755 74756 | } i = PENDING_BYTE_PAGE(pBt); if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); /* Check the integrity of the freelist */ if( bCkFreelist ){ sCheck.zPfx = "Main freelist: "; checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), get4byte(&pBt->pPage1->aData[36])); sCheck.zPfx = 0; } /* Check all the tables. */ #ifndef SQLITE_OMIT_AUTOVACUUM if( !bPartial ){ if( pBt->autoVacuum ){ Pgno mx = 0; Pgno mxInHdr; for(i=0; (int)i<nRoot; i++) if( mx<aRoot[i] ) mx = aRoot[i]; mxInHdr = get4byte(&pBt->pPage1->aData[52]); if( mx!=mxInHdr ){ checkAppendMsg(&sCheck, "max rootpage (%d) disagrees with header (%d)", mx, mxInHdr ); } }else if( get4byte(&pBt->pPage1->aData[64])!=0 ){ checkAppendMsg(&sCheck, "incremental_vacuum enabled with a max rootpage of zero" ); } } #endif testcase( pBt->db->flags & SQLITE_CellSizeCk ); pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; for(i=0; (int)i<nRoot && sCheck.mxErr; i++){ i64 notUsed; if( aRoot[i]==0 ) continue; #ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){ checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); } #endif checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); } pBt->db->flags = savedDbFlags; /* Make sure every page in the file is referenced */ if( !bPartial ){ for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ #ifdef SQLITE_OMIT_AUTOVACUUM if( getPageReferenced(&sCheck, i)==0 ){ checkAppendMsg(&sCheck, "Page %d is never used", i); } #else /* If the database supports auto-vacuum, make sure no tables contain ** references to pointer-map pages. */ if( getPageReferenced(&sCheck, i)==0 && (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ checkAppendMsg(&sCheck, "Page %d is never used", i); } if( getPageReferenced(&sCheck, i)!=0 && (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i); } #endif } } /* Clean up and report errors. */ integrity_ck_cleanup: sqlite3PageFree(sCheck.heap); sqlite3_free(sCheck.aPgRef); |
︙ | ︙ | |||
75792 75793 75794 75795 75796 75797 75798 | /* ** Render a Mem object which is one of MEM_Int, MEM_Real, or MEM_IntReal ** into a buffer. */ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ StrAccum acc; assert( p->flags & (MEM_Int|MEM_Real|MEM_IntReal) ); | | > > > > > > | | | > > | < > | | > | 75930 75931 75932 75933 75934 75935 75936 75937 75938 75939 75940 75941 75942 75943 75944 75945 75946 75947 75948 75949 75950 75951 75952 75953 75954 75955 75956 75957 75958 75959 75960 75961 75962 | /* ** Render a Mem object which is one of MEM_Int, MEM_Real, or MEM_IntReal ** into a buffer. */ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ StrAccum acc; assert( p->flags & (MEM_Int|MEM_Real|MEM_IntReal) ); assert( sz>22 ); if( p->flags & MEM_Int ){ #if GCC_VERSION>=7000000 /* Work-around for GCC bug ** https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96270 */ i64 x; assert( (p->flags&MEM_Int)*2==sizeof(x) ); memcpy(&x, (char*)&p->u, (p->flags&MEM_Int)*2); sqlite3Int64ToText(x, zBuf); #else sqlite3Int64ToText(p->u.i, zBuf); #endif }else{ sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0); sqlite3_str_appendf(&acc, "%!.15g", (p->flags & MEM_IntReal)!=0 ? (double)p->u.i : p->u.r); assert( acc.zText==zBuf && acc.mxAlloc<=0 ); zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */ } } #ifdef SQLITE_DEBUG /* ** Validity checks on pMem. pMem holds a string. ** ** (1) Check that string value of pMem agrees with its integer or real value. |
︙ | ︙ | |||
78395 78396 78397 78398 78399 78400 78401 | */ if( pOp->opcode<=SQLITE_MX_JUMP_OPCODE ){ /* NOTE: Be sure to update mkopcodeh.tcl when adding or removing ** cases from this switch! */ switch( pOp->opcode ){ case OP_Transaction: { if( pOp->p2!=0 ) p->readOnly = 0; | | | 78542 78543 78544 78545 78546 78547 78548 78549 78550 78551 78552 78553 78554 78555 78556 | */ if( pOp->opcode<=SQLITE_MX_JUMP_OPCODE ){ /* NOTE: Be sure to update mkopcodeh.tcl when adding or removing ** cases from this switch! */ switch( pOp->opcode ){ case OP_Transaction: { if( pOp->p2!=0 ) p->readOnly = 0; /* no break */ deliberate_fall_through } case OP_AutoCommit: case OP_Savepoint: { p->bIsReader = 1; break; } #ifndef SQLITE_OMIT_WAL |
︙ | ︙ | |||
78442 78443 78444 78445 78446 78447 78448 78449 78450 78451 78452 78453 78454 78455 | case OP_VFilter: { int n; assert( (pOp - p->aOp) >= 3 ); assert( pOp[-1].opcode==OP_Integer ); n = pOp[-1].p1; if( n>nMaxArgs ) nMaxArgs = n; /* Fall through into the default case */ } #endif default: { if( pOp->p2<0 ){ /* The mkopcodeh.tcl script has so arranged things that the only ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to ** have non-negative values for P2. */ | > | 78589 78590 78591 78592 78593 78594 78595 78596 78597 78598 78599 78600 78601 78602 78603 | case OP_VFilter: { int n; assert( (pOp - p->aOp) >= 3 ); assert( pOp[-1].opcode==OP_Integer ); n = pOp[-1].p1; if( n>nMaxArgs ) nMaxArgs = n; /* Fall through into the default case */ /* no break */ deliberate_fall_through } #endif default: { if( pOp->p2<0 ){ /* The mkopcodeh.tcl script has so arranged things that the only ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to ** have non-negative values for P2. */ |
︙ | ︙ | |||
79299 79300 79301 79302 79303 79304 79305 | case P4_VTAB: { sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab; sqlite3_str_appendf(&x, "vtab:%p", pVtab); break; } #endif case P4_INTARRAY: { | | | | | | 79447 79448 79449 79450 79451 79452 79453 79454 79455 79456 79457 79458 79459 79460 79461 79462 79463 79464 79465 79466 | case P4_VTAB: { sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab; sqlite3_str_appendf(&x, "vtab:%p", pVtab); break; } #endif case P4_INTARRAY: { u32 i; u32 *ai = pOp->p4.ai; u32 n = ai[0]; /* The first element of an INTARRAY is always the ** count of the number of elements to follow */ for(i=1; i<=n; i++){ sqlite3_str_appendf(&x, "%c%u", (i==1 ? '[' : ','), ai[i]); } sqlite3_str_append(&x, "]", 1); break; } case P4_SUBPROGRAM: { zP4 = "program"; break; |
︙ | ︙ | |||
81148 81149 81150 81151 81152 81153 81154 | ** MoveTo now. If no move is pending, check to see if the row has been ** deleted out from under the cursor and if it has, mark the row as ** a NULL row. ** ** If the cursor is already pointing to the correct row and that row has ** not been deleted out from under the cursor, then this routine is a no-op. */ | | | | 81296 81297 81298 81299 81300 81301 81302 81303 81304 81305 81306 81307 81308 81309 81310 81311 81312 81313 81314 | ** MoveTo now. If no move is pending, check to see if the row has been ** deleted out from under the cursor and if it has, mark the row as ** a NULL row. ** ** If the cursor is already pointing to the correct row and that row has ** not been deleted out from under the cursor, then this routine is a no-op. */ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ VdbeCursor *p = *pp; assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO ); if( p->deferredMoveto ){ u32 iMap; if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){ *pp = p->pAltCursor; *piCol = iMap - 1; return SQLITE_OK; } return sqlite3VdbeFinishMoveto(p); } |
︙ | ︙ | |||
82908 82909 82910 82911 82912 82913 82914 | iElapse = (iNow - p->startTime)*1000000; #ifndef SQLITE_OMIT_DEPRECATED if( db->xProfile ){ db->xProfile(db->pProfileArg, p->zSql, iElapse); } #endif if( db->mTrace & SQLITE_TRACE_PROFILE ){ | | | 83056 83057 83058 83059 83060 83061 83062 83063 83064 83065 83066 83067 83068 83069 83070 | iElapse = (iNow - p->startTime)*1000000; #ifndef SQLITE_OMIT_DEPRECATED if( db->xProfile ){ db->xProfile(db->pProfileArg, p->zSql, iElapse); } #endif if( db->mTrace & SQLITE_TRACE_PROFILE ){ db->trace.xV2(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse); } p->startTime = 0; } /* ** The checkProfileCallback(DB,P) macro checks to see if a profile callback ** is needed, and it invokes the callback if it is needed. */ |
︙ | ︙ | |||
86208 86209 86210 86211 86212 86213 86214 86215 86216 86217 86218 86219 86220 86221 | case OP_HaltIfNull: { /* in3 */ pIn3 = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); } #endif if( (pIn3->flags & MEM_Null)==0 ) break; /* Fall through into OP_Halt */ } /* Opcode: Halt P1 P2 * P4 P5 ** ** Exit immediately. All open cursors, etc are closed ** automatically. ** | > | 86356 86357 86358 86359 86360 86361 86362 86363 86364 86365 86366 86367 86368 86369 86370 | case OP_HaltIfNull: { /* in3 */ pIn3 = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); } #endif if( (pIn3->flags & MEM_Null)==0 ) break; /* Fall through into OP_Halt */ /* no break */ deliberate_fall_through } /* Opcode: Halt P1 P2 * P4 P5 ** ** Exit immediately. All open cursors, etc are closed ** automatically. ** |
︙ | ︙ | |||
86378 86379 86380 86381 86382 86383 86384 86385 86386 86387 86388 86389 86390 86391 | #endif if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } pOp->opcode = OP_String; assert( rc==SQLITE_OK ); /* Fall through to the next case, OP_String */ } /* Opcode: String P1 P2 P3 P4 P5 ** Synopsis: r[P2]='P4' (len=P1) ** ** The string value P4 of length P1 (bytes) is stored in register P2. ** | > | 86527 86528 86529 86530 86531 86532 86533 86534 86535 86536 86537 86538 86539 86540 86541 | #endif if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } pOp->opcode = OP_String; assert( rc==SQLITE_OK ); /* Fall through to the next case, OP_String */ /* no break */ deliberate_fall_through } /* Opcode: String P1 P2 P3 P4 P5 ** Synopsis: r[P2]='P4' (len=P1) ** ** The string value P4 of length P1 (bytes) is stored in register P2. ** |
︙ | ︙ | |||
86689 86690 86691 86692 86693 86694 86695 | ** OP_SCopy dependencies. */ pMem[i].pScopyFrom = 0; #endif } if( db->mallocFailed ) goto no_mem; if( db->mTrace & SQLITE_TRACE_ROW ){ | | | 86839 86840 86841 86842 86843 86844 86845 86846 86847 86848 86849 86850 86851 86852 86853 | ** OP_SCopy dependencies. */ pMem[i].pScopyFrom = 0; #endif } if( db->mallocFailed ) goto no_mem; if( db->mTrace & SQLITE_TRACE_ROW ){ db->trace.xV2(SQLITE_TRACE_ROW, db->pTraceArg, p, 0); } /* Return SQLITE_ROW */ p->pc = (int)(pOp - aOp) + 1; rc = SQLITE_ROW; |
︙ | ︙ | |||
87425 87426 87427 87428 87429 87430 87431 | */ case OP_Compare: { int n; int i; int p1; int p2; const KeyInfo *pKeyInfo; | | | | | | 87575 87576 87577 87578 87579 87580 87581 87582 87583 87584 87585 87586 87587 87588 87589 87590 87591 87592 87593 87594 87595 87596 87597 87598 87599 87600 87601 87602 87603 87604 87605 87606 87607 87608 87609 87610 87611 87612 87613 87614 87615 87616 87617 87618 87619 87620 87621 | */ case OP_Compare: { int n; int i; int p1; int p2; const KeyInfo *pKeyInfo; u32 idx; CollSeq *pColl; /* Collating sequence to use on this term */ int bRev; /* True for DESCENDING sort order */ u32 *aPermute; /* The permutation */ if( (pOp->p5 & OPFLAG_PERMUTE)==0 ){ aPermute = 0; }else{ assert( pOp>aOp ); assert( pOp[-1].opcode==OP_Permutation ); assert( pOp[-1].p4type==P4_INTARRAY ); aPermute = pOp[-1].p4.ai + 1; assert( aPermute!=0 ); } n = pOp->p3; pKeyInfo = pOp->p4.pKeyInfo; assert( n>0 ); assert( pKeyInfo!=0 ); p1 = pOp->p1; p2 = pOp->p2; #ifdef SQLITE_DEBUG if( aPermute ){ int k, mx = 0; for(k=0; k<n; k++) if( aPermute[k]>(u32)mx ) mx = aPermute[k]; assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 ); assert( p2>0 && p2+mx<=(p->nMem+1 - p->nCursor)+1 ); }else{ assert( p1>0 && p1+n<=(p->nMem+1 - p->nCursor)+1 ); assert( p2>0 && p2+n<=(p->nMem+1 - p->nCursor)+1 ); } #endif /* SQLITE_DEBUG */ for(i=0; i<n; i++){ idx = aPermute ? aPermute[i] : (u32)i; assert( memIsValid(&aMem[p1+idx]) ); assert( memIsValid(&aMem[p2+idx]) ); REGISTER_TRACE(p1+idx, &aMem[p1+idx]); REGISTER_TRACE(p2+idx, &aMem[p2+idx]); assert( i<pKeyInfo->nKeyField ); pColl = pKeyInfo->aColl[i]; bRev = (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC); |
︙ | ︙ | |||
87768 87769 87770 87771 87772 87773 87774 | ** ** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 then ** the result is guaranteed to only be used as the argument of a length() ** or typeof() function, respectively. The loading of large blobs can be ** skipped for length() and all content loading can be skipped for typeof(). */ case OP_Column: { | | | | | 87918 87919 87920 87921 87922 87923 87924 87925 87926 87927 87928 87929 87930 87931 87932 87933 87934 87935 87936 87937 87938 87939 87940 87941 87942 87943 87944 87945 87946 87947 87948 87949 87950 87951 87952 87953 87954 87955 87956 87957 87958 87959 87960 87961 87962 | ** ** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 then ** the result is guaranteed to only be used as the argument of a length() ** or typeof() function, respectively. The loading of large blobs can be ** skipped for length() and all content loading can be skipped for typeof(). */ case OP_Column: { u32 p2; /* column number to retrieve */ VdbeCursor *pC; /* The VDBE cursor */ BtCursor *pCrsr; /* The BTree cursor */ u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ int len; /* The length of the serialized data for the column */ int i; /* Loop counter */ Mem *pDest; /* Where to write the extracted value */ Mem sMem; /* For storing the record being decoded */ const u8 *zData; /* Part of the record being decoded */ const u8 *zHdr; /* Next unparsed byte of the header */ const u8 *zEndHdr; /* Pointer to first byte after the header */ u64 offset64; /* 64-bit offset */ u32 t; /* A type code from the record header */ Mem *pReg; /* PseudoTable input register */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); p2 = (u32)pOp->p2; /* If the cursor cache is stale (meaning it is not currently point at ** the correct row) then bring it up-to-date by doing the necessary ** B-Tree seek. */ rc = sqlite3VdbeCursorMoveto(&pC, &p2); if( rc ) goto abort_due_to_error; assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); assert( pC!=0 ); assert( p2<(u32)pC->nField ); aOffset = pC->aOffset; assert( pC->eCurType!=CURTYPE_VTAB ); assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); assert( pC->eCurType!=CURTYPE_SORTER ); if( pC->cacheStatus!=p->cacheCtr ){ /*OPTIMIZATION-IF-FALSE*/ if( pC->nullRow ){ |
︙ | ︙ | |||
87913 87914 87915 87916 87917 87918 87919 | offset64 += sqlite3VdbeOneByteSerialTypeLen(t); }else{ zHdr += sqlite3GetVarint32(zHdr, &t); pC->aType[i] = t; offset64 += sqlite3VdbeSerialTypeLen(t); } aOffset[++i] = (u32)(offset64 & 0xffffffff); | | | 88063 88064 88065 88066 88067 88068 88069 88070 88071 88072 88073 88074 88075 88076 88077 | offset64 += sqlite3VdbeOneByteSerialTypeLen(t); }else{ zHdr += sqlite3GetVarint32(zHdr, &t); pC->aType[i] = t; offset64 += sqlite3VdbeSerialTypeLen(t); } aOffset[++i] = (u32)(offset64 & 0xffffffff); }while( (u32)i<=p2 && zHdr<zEndHdr ); /* The record is corrupt if any of the following are true: ** (1) the bytes of the header extend past the declared header size ** (2) the entire header was used but not all data was used ** (3) the end of the data extends beyond the end of the record. */ if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset64!=pC->payloadSize)) |
︙ | ︙ | |||
88937 88938 88939 88940 88941 88942 88943 | ** in read/write mode. ** ** See also: OP_OpenRead, OP_ReopenIdx */ case OP_ReopenIdx: { int nField; KeyInfo *pKeyInfo; | | | 89087 89088 89089 89090 89091 89092 89093 89094 89095 89096 89097 89098 89099 89100 89101 | ** in read/write mode. ** ** See also: OP_OpenRead, OP_ReopenIdx */ case OP_ReopenIdx: { int nField; KeyInfo *pKeyInfo; u32 p2; int iDb; int wrFlag; Btree *pX; VdbeCursor *pCur; Db *pDb; assert( pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ ); |
︙ | ︙ | |||
88968 88969 88970 88971 88972 88973 88974 | if( p->expired==1 ){ rc = SQLITE_ABORT_ROLLBACK; goto abort_due_to_error; } nField = 0; pKeyInfo = 0; | | | | 89118 89119 89120 89121 89122 89123 89124 89125 89126 89127 89128 89129 89130 89131 89132 89133 89134 89135 89136 89137 89138 89139 89140 89141 89142 89143 89144 89145 89146 89147 89148 89149 89150 89151 | if( p->expired==1 ){ rc = SQLITE_ABORT_ROLLBACK; goto abort_due_to_error; } nField = 0; pKeyInfo = 0; p2 = (u32)pOp->p2; iDb = pOp->p3; assert( iDb>=0 && iDb<db->nDb ); assert( DbMaskTest(p->btreeMask, iDb) ); pDb = &db->aDb[iDb]; pX = pDb->pBt; assert( pX!=0 ); if( pOp->opcode==OP_OpenWrite ){ assert( OPFLAG_FORDELETE==BTREE_FORDELETE ); wrFlag = BTREE_WRCSR | (pOp->p5 & OPFLAG_FORDELETE); assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( pDb->pSchema->file_format < p->minWriteFileFormat ){ p->minWriteFileFormat = pDb->pSchema->file_format; } }else{ wrFlag = 0; } if( pOp->p5 & OPFLAG_P2ISREG ){ assert( p2>0 ); assert( p2<=(u32)(p->nMem+1 - p->nCursor) ); assert( pOp->opcode==OP_OpenWrite ); pIn2 = &aMem[p2]; assert( memIsValid(pIn2) ); assert( (pIn2->flags & MEM_Int)!=0 ); sqlite3VdbeMemIntegerify(pIn2); p2 = (int)pIn2->u.i; /* The p2 value always comes from a prior OP_CreateBtree opcode and |
︙ | ︙ | |||
89140 89141 89142 89143 89144 89145 89146 | /* If a transient index is required, create it by calling ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before ** opening it. If a transient table is required, just use the ** automatically created table with root-page 1 (an BLOB_INTKEY table). */ if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ assert( pOp->p4type==P4_KEYINFO ); | | | 89290 89291 89292 89293 89294 89295 89296 89297 89298 89299 89300 89301 89302 89303 89304 | /* If a transient index is required, create it by calling ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before ** opening it. If a transient table is required, just use the ** automatically created table with root-page 1 (an BLOB_INTKEY table). */ if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ assert( pOp->p4type==P4_KEYINFO ); rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot, BTREE_BLOBKEY | pOp->p5); if( rc==SQLITE_OK ){ assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); assert( pKeyInfo->db==db ); assert( pKeyInfo->enc==ENC(db) ); rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, pKeyInfo, pCx->uc.pCursor); |
︙ | ︙ | |||
89682 89683 89684 89685 89686 89687 89688 89689 89690 89691 89692 89693 89694 89695 | case OP_IfNoHope: { /* jump, in3 */ VdbeCursor *pC; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); if( pC->seekHit ) break; /* Fall through into OP_NotFound */ } case OP_NoConflict: /* jump, in3 */ case OP_NotFound: /* jump, in3 */ case OP_Found: { /* jump, in3 */ int alreadyExists; int takeJump; int ii; | > | 89832 89833 89834 89835 89836 89837 89838 89839 89840 89841 89842 89843 89844 89845 89846 | case OP_IfNoHope: { /* jump, in3 */ VdbeCursor *pC; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); if( pC->seekHit ) break; /* Fall through into OP_NotFound */ /* no break */ deliberate_fall_through } case OP_NoConflict: /* jump, in3 */ case OP_NotFound: /* jump, in3 */ case OP_Found: { /* jump, in3 */ int alreadyExists; int takeJump; int ii; |
︙ | ︙ | |||
89836 89837 89838 89839 89840 89841 89842 89843 89844 89845 89846 89847 89848 89849 | Mem x = pIn3[0]; applyAffinity(&x, SQLITE_AFF_NUMERIC, encoding); if( (x.flags & MEM_Int)==0 ) goto jump_to_p2; iKey = x.u.i; goto notExistsWithKey; } /* Fall through into OP_NotExists */ case OP_NotExists: /* jump, in3 */ pIn3 = &aMem[pOp->p3]; assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid ); assert( pOp->p1>=0 && pOp->p1<p->nCursor ); iKey = pIn3->u.i; notExistsWithKey: pC = p->apCsr[pOp->p1]; | > | 89987 89988 89989 89990 89991 89992 89993 89994 89995 89996 89997 89998 89999 90000 90001 | Mem x = pIn3[0]; applyAffinity(&x, SQLITE_AFF_NUMERIC, encoding); if( (x.flags & MEM_Int)==0 ) goto jump_to_p2; iKey = x.u.i; goto notExistsWithKey; } /* Fall through into OP_NotExists */ /* no break */ deliberate_fall_through case OP_NotExists: /* jump, in3 */ pIn3 = &aMem[pOp->p3]; assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid ); assert( pOp->p1>=0 && pOp->p1<p->nCursor ); iKey = pIn3->u.i; notExistsWithKey: pC = p->apCsr[pOp->p1]; |
︙ | ︙ | |||
90404 90405 90406 90407 90408 90409 90410 | ** If this where not the case, on of the following assert()s ** would fail. Should this ever change (because of changes in the code ** generator) then the fix would be to insert a call to ** sqlite3VdbeCursorMoveto(). */ assert( pC->deferredMoveto==0 ); assert( sqlite3BtreeCursorIsValid(pCrsr) ); | < < < < | 90556 90557 90558 90559 90560 90561 90562 90563 90564 90565 90566 90567 90568 90569 | ** If this where not the case, on of the following assert()s ** would fail. Should this ever change (because of changes in the code ** generator) then the fix would be to insert a call to ** sqlite3VdbeCursorMoveto(). */ assert( pC->deferredMoveto==0 ); assert( sqlite3BtreeCursorIsValid(pCrsr) ); n = sqlite3BtreePayloadSize(pCrsr); if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } testcase( n==0 ); rc = sqlite3VdbeMemFromBtreeZeroOffset(pCrsr, n, pOut); |
︙ | ︙ | |||
90611 90612 90613 90614 90615 90616 90617 90618 90619 90620 90621 90622 90623 90624 | case OP_Sort: { /* jump */ #ifdef SQLITE_TEST sqlite3_sort_count++; sqlite3_search_count--; #endif p->aCounter[SQLITE_STMTSTATUS_SORT]++; /* Fall through into OP_Rewind */ } /* Opcode: Rewind P1 P2 * * * ** ** The next use of the Rowid or Column or Next instruction for P1 ** will refer to the first entry in the database table or index. ** If the table or index is empty, jump immediately to P2. ** If the table or index is not empty, fall through to the following | > | 90759 90760 90761 90762 90763 90764 90765 90766 90767 90768 90769 90770 90771 90772 90773 | case OP_Sort: { /* jump */ #ifdef SQLITE_TEST sqlite3_sort_count++; sqlite3_search_count--; #endif p->aCounter[SQLITE_STMTSTATUS_SORT]++; /* Fall through into OP_Rewind */ /* no break */ deliberate_fall_through } /* Opcode: Rewind P1 P2 * * * ** ** The next use of the Rowid or Column or Next instruction for P1 ** will refer to the first entry in the database table or index. ** If the table or index is empty, jump immediately to P2. ** If the table or index is not empty, fall through to the following |
︙ | ︙ | |||
91177 91178 91179 91180 91181 91182 91183 | int nChange; sqlite3VdbeIncrWriteCounter(p, 0); nChange = 0; assert( p->readOnly==0 ); assert( DbMaskTest(p->btreeMask, pOp->p2) ); rc = sqlite3BtreeClearTable( | | | 91326 91327 91328 91329 91330 91331 91332 91333 91334 91335 91336 91337 91338 91339 91340 | int nChange; sqlite3VdbeIncrWriteCounter(p, 0); nChange = 0; assert( p->readOnly==0 ); assert( DbMaskTest(p->btreeMask, pOp->p2) ); rc = sqlite3BtreeClearTable( db->aDb[pOp->p2].pBt, (u32)pOp->p1, (pOp->p3 ? &nChange : 0) ); if( pOp->p3 ){ p->nChange += nChange; if( pOp->p3>0 ){ assert( memIsValid(&aMem[pOp->p3]) ); memAboutToChange(p, &aMem[pOp->p3]); aMem[pOp->p3].u.i += nChange; |
︙ | ︙ | |||
91226 91227 91228 91229 91230 91231 91232 | ** Allocate a new b-tree in the main database file if P1==0 or in the ** TEMP database file if P1==1 or in an attached database if ** P1>1. The P3 argument must be 1 (BTREE_INTKEY) for a rowid table ** it must be 2 (BTREE_BLOBKEY) for an index or WITHOUT ROWID table. ** The root page number of the new b-tree is stored in register P2. */ case OP_CreateBtree: { /* out2 */ | | | 91375 91376 91377 91378 91379 91380 91381 91382 91383 91384 91385 91386 91387 91388 91389 | ** Allocate a new b-tree in the main database file if P1==0 or in the ** TEMP database file if P1==1 or in an attached database if ** P1>1. The P3 argument must be 1 (BTREE_INTKEY) for a rowid table ** it must be 2 (BTREE_BLOBKEY) for an index or WITHOUT ROWID table. ** The root page number of the new b-tree is stored in register P2. */ case OP_CreateBtree: { /* out2 */ Pgno pgno; Db *pDb; sqlite3VdbeIncrWriteCounter(p, 0); pOut = out2Prerelease(p, pOp); pgno = 0; assert( pOp->p3==BTREE_INTKEY || pOp->p3==BTREE_BLOBKEY ); assert( pOp->p1>=0 && pOp->p1<db->nDb ); |
︙ | ︙ | |||
91301 91302 91303 91304 91305 91306 91307 91308 91309 91310 91311 91312 91313 91314 | #endif { zSchema = DFLT_SCHEMA_TABLE; initData.db = db; initData.iDb = iDb; initData.pzErrMsg = &p->zErrMsg; initData.mInitFlags = 0; zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\".%s WHERE %s ORDER BY rowid", db->aDb[iDb].zDbSName, zSchema, pOp->p4.z); if( zSql==0 ){ rc = SQLITE_NOMEM_BKPT; }else{ assert( db->init.busy==0 ); | > | 91450 91451 91452 91453 91454 91455 91456 91457 91458 91459 91460 91461 91462 91463 91464 | #endif { zSchema = DFLT_SCHEMA_TABLE; initData.db = db; initData.iDb = iDb; initData.pzErrMsg = &p->zErrMsg; initData.mInitFlags = 0; initData.mxPage = sqlite3BtreeLastPage(db->aDb[iDb].pBt); zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\".%s WHERE %s ORDER BY rowid", db->aDb[iDb].zDbSName, zSchema, pOp->p4.z); if( zSql==0 ){ rc = SQLITE_NOMEM_BKPT; }else{ assert( db->init.busy==0 ); |
︙ | ︙ | |||
91414 91415 91416 91417 91418 91419 91420 | ** If P5 is not zero, the check is done on the auxiliary database ** file, not the main database file. ** ** This opcode is used to implement the integrity_check pragma. */ case OP_IntegrityCk: { int nRoot; /* Number of tables to check. (Number of root pages.) */ | | | | 91564 91565 91566 91567 91568 91569 91570 91571 91572 91573 91574 91575 91576 91577 91578 91579 91580 91581 91582 91583 91584 91585 91586 91587 | ** If P5 is not zero, the check is done on the auxiliary database ** file, not the main database file. ** ** This opcode is used to implement the integrity_check pragma. */ case OP_IntegrityCk: { int nRoot; /* Number of tables to check. (Number of root pages.) */ Pgno *aRoot; /* Array of rootpage numbers for tables to be checked */ int nErr; /* Number of errors reported */ char *z; /* Text of the error report */ Mem *pnErr; /* Register keeping track of errors remaining */ assert( p->bIsReader ); nRoot = pOp->p2; aRoot = pOp->p4.ai; assert( nRoot>0 ); assert( aRoot[0]==(Pgno)nRoot ); assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pnErr = &aMem[pOp->p3]; assert( (pnErr->flags & MEM_Int)!=0 ); assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); pIn1 = &aMem[pOp->p1]; assert( pOp->p5<db->nDb ); assert( DbMaskTest(p->btreeMask, pOp->p5) ); |
︙ | ︙ | |||
91963 91964 91965 91966 91967 91968 91969 91970 91971 91972 91973 91974 91975 91976 | pOp->p4.pCtx = pCtx; /* OP_AggInverse must have P1==1 and OP_AggStep must have P1==0 */ assert( pOp->p1==(pOp->opcode==OP_AggInverse) ); pOp->opcode = OP_AggStep1; /* Fall through into OP_AggStep */ } case OP_AggStep1: { int i; sqlite3_context *pCtx; Mem *pMem; assert( pOp->p4type==P4_FUNCCTX ); | > | 92113 92114 92115 92116 92117 92118 92119 92120 92121 92122 92123 92124 92125 92126 92127 | pOp->p4.pCtx = pCtx; /* OP_AggInverse must have P1==1 and OP_AggStep must have P1==0 */ assert( pOp->p1==(pOp->opcode==OP_AggInverse) ); pOp->opcode = OP_AggStep1; /* Fall through into OP_AggStep */ /* no break */ deliberate_fall_through } case OP_AggStep1: { int i; sqlite3_context *pCtx; Mem *pMem; assert( pOp->p4type==P4_FUNCCTX ); |
︙ | ︙ | |||
92952 92953 92954 92955 92956 92957 92958 | #ifndef SQLITE_OMIT_TRACE if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0 && !p->doingRerun && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 ){ #ifndef SQLITE_OMIT_DEPRECATED if( db->mTrace & SQLITE_TRACE_LEGACY ){ | < | | | | 93103 93104 93105 93106 93107 93108 93109 93110 93111 93112 93113 93114 93115 93116 93117 93118 93119 93120 93121 93122 93123 93124 93125 93126 93127 | #ifndef SQLITE_OMIT_TRACE if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0 && !p->doingRerun && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 ){ #ifndef SQLITE_OMIT_DEPRECATED if( db->mTrace & SQLITE_TRACE_LEGACY ){ char *z = sqlite3VdbeExpandSql(p, zTrace); db->trace.xLegacy(db->pTraceArg, z); sqlite3_free(z); }else #endif if( db->nVdbeExec>1 ){ char *z = sqlite3MPrintf(db, "-- %s", zTrace); (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, z); sqlite3DbFree(db, z); }else{ (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace); } } #ifdef SQLITE_USE_FCNTL_TRACE zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql); if( zTrace ){ int j; for(j=0; j<db->nDb; j++){ |
︙ | ︙ | |||
96699 96700 96701 96702 96703 96704 96705 | if( i==4 ){ i = 8; }else{ if( i<=2 && pCur->zType==0 ){ Schema *pSchema; HashElem *k; int iDb = pOp->p3; | | | 96849 96850 96851 96852 96853 96854 96855 96856 96857 96858 96859 96860 96861 96862 96863 | if( i==4 ){ i = 8; }else{ if( i<=2 && pCur->zType==0 ){ Schema *pSchema; HashElem *k; int iDb = pOp->p3; Pgno iRoot = (Pgno)pOp->p2; sqlite3 *db = pVTab->db; pSchema = db->aDb[iDb].pSchema; pCur->zSchema = db->aDb[iDb].zDbSName; for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ Table *pTab = (Table*)sqliteHashData(k); if( !IsVirtual(pTab) && pTab->tnum==iRoot ){ pCur->zName = pTab->zName; |
︙ | ︙ | |||
97286 97287 97288 97289 97290 97291 97292 | if( nSpill>0 ){ p->nChunkSize = nSpill; }else{ p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk); assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) ); } | | | 97436 97437 97438 97439 97440 97441 97442 97443 97444 97445 97446 97447 97448 97449 97450 | if( nSpill>0 ){ p->nChunkSize = nSpill; }else{ p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk); assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) ); } pJfd->pMethods = (const sqlite3_io_methods*)&MemJournalMethods; p->nSpill = nSpill; p->flags = flags; p->zJournal = zName; p->pVfs = pVfs; return SQLITE_OK; } |
︙ | ︙ | |||
97312 97313 97314 97315 97316 97317 97318 | ** in-memory-only journal file (i.e. is one that was opened with a +ve ** nSpill parameter or as SQLITE_OPEN_MAIN_JOURNAL), and the underlying ** file has not yet been created, create it now. */ SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *pJfd){ int rc = SQLITE_OK; MemJournal *p = (MemJournal*)pJfd; | | | 97462 97463 97464 97465 97466 97467 97468 97469 97470 97471 97472 97473 97474 97475 97476 | ** in-memory-only journal file (i.e. is one that was opened with a +ve ** nSpill parameter or as SQLITE_OPEN_MAIN_JOURNAL), and the underlying ** file has not yet been created, create it now. */ SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *pJfd){ int rc = SQLITE_OK; MemJournal *p = (MemJournal*)pJfd; if( pJfd->pMethods==&MemJournalMethods && ( #ifdef SQLITE_ENABLE_ATOMIC_WRITE p->nSpill>0 #else /* While this appears to not be possible without ATOMIC_WRITE, the ** paths are complex, so it seems prudent to leave the test in as ** a NEVER(), in case our analysis is subtly flawed. */ NEVER(p->nSpill>0) |
︙ | ︙ | |||
98685 98686 98687 98688 98689 98690 98691 | if( rc==WRC_Abort ) return WRC_Abort; if( pRight->op==TK_TRUEFALSE ){ pExpr->op2 = pExpr->op; pExpr->op = TK_TRUTH; return WRC_Continue; } } | | | 98835 98836 98837 98838 98839 98840 98841 98842 98843 98844 98845 98846 98847 98848 98849 | if( rc==WRC_Abort ) return WRC_Abort; if( pRight->op==TK_TRUEFALSE ){ pExpr->op2 = pExpr->op; pExpr->op = TK_TRUTH; return WRC_Continue; } } /* no break */ deliberate_fall_through } case TK_BETWEEN: case TK_EQ: case TK_NE: case TK_LT: case TK_LE: case TK_GT: |
︙ | ︙ | |||
101589 101590 101591 101592 101593 101594 101595 | } case TK_ID: /* Convert "true" or "false" in a DEFAULT clause into the ** appropriate TK_TRUEFALSE operator */ if( sqlite3ExprIdToTrueFalse(pExpr) ){ return WRC_Prune; } | | | | | 101739 101740 101741 101742 101743 101744 101745 101746 101747 101748 101749 101750 101751 101752 101753 101754 101755 101756 101757 101758 101759 101760 101761 101762 101763 101764 101765 101766 101767 101768 101769 101770 101771 101772 101773 101774 101775 101776 101777 101778 101779 101780 101781 101782 101783 101784 101785 101786 101787 101788 | } case TK_ID: /* Convert "true" or "false" in a DEFAULT clause into the ** appropriate TK_TRUEFALSE operator */ if( sqlite3ExprIdToTrueFalse(pExpr) ){ return WRC_Prune; } /* no break */ deliberate_fall_through case TK_COLUMN: case TK_AGG_FUNCTION: case TK_AGG_COLUMN: testcase( pExpr->op==TK_ID ); testcase( pExpr->op==TK_COLUMN ); testcase( pExpr->op==TK_AGG_FUNCTION ); testcase( pExpr->op==TK_AGG_COLUMN ); if( ExprHasProperty(pExpr, EP_FixedCol) && pWalker->eCode!=2 ){ return WRC_Continue; } if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){ return WRC_Continue; } /* no break */ deliberate_fall_through case TK_IF_NULL_ROW: case TK_REGISTER: case TK_DOT: testcase( pExpr->op==TK_REGISTER ); testcase( pExpr->op==TK_IF_NULL_ROW ); testcase( pExpr->op==TK_DOT ); pWalker->eCode = 0; return WRC_Abort; case TK_VARIABLE: if( pWalker->eCode==5 ){ /* Silently convert bound parameters that appear inside of CREATE ** statements into a NULL when parsing the CREATE statement text out ** of the sqlite_schema table */ pExpr->op = TK_NULL; }else if( pWalker->eCode==4 ){ /* A bound parameter in a CREATE statement that originates from ** sqlite3_prepare() causes an error */ pWalker->eCode = 0; return WRC_Abort; } /* no break */ deliberate_fall_through default: testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */ testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */ return WRC_Continue; } } static int exprIsConst(Expr *p, int initFlag, int iCur){ |
︙ | ︙ | |||
103396 103397 103398 103399 103400 103401 103402 103403 103404 103405 103406 103407 103408 103409 | if( pTab->aCol[pCol->iColumn].affinity==SQLITE_AFF_REAL ){ sqlite3VdbeAddOp1(v, OP_RealAffinity, target); } } return target; } /* Otherwise, fall thru into the TK_COLUMN case */ } case TK_COLUMN: { int iTab = pExpr->iTable; int iReg; if( ExprHasProperty(pExpr, EP_FixedCol) ){ /* This COLUMN expression is really a constant due to WHERE clause ** constraints, and that constant is coded by the pExpr->pLeft | > | 103546 103547 103548 103549 103550 103551 103552 103553 103554 103555 103556 103557 103558 103559 103560 | if( pTab->aCol[pCol->iColumn].affinity==SQLITE_AFF_REAL ){ sqlite3VdbeAddOp1(v, OP_RealAffinity, target); } } return target; } /* Otherwise, fall thru into the TK_COLUMN case */ /* no break */ deliberate_fall_through } case TK_COLUMN: { int iTab = pExpr->iTable; int iReg; if( ExprHasProperty(pExpr, EP_FixedCol) ){ /* This COLUMN expression is really a constant due to WHERE clause ** constraints, and that constant is coded by the pExpr->pLeft |
︙ | ︙ | |||
104461 104462 104463 104464 104465 104466 104467 | } case TK_IS: case TK_ISNOT: testcase( op==TK_IS ); testcase( op==TK_ISNOT ); op = (op==TK_IS) ? TK_EQ : TK_NE; jumpIfNull = SQLITE_NULLEQ; | | | 104612 104613 104614 104615 104616 104617 104618 104619 104620 104621 104622 104623 104624 104625 104626 | } case TK_IS: case TK_ISNOT: testcase( op==TK_IS ); testcase( op==TK_ISNOT ); op = (op==TK_IS) ? TK_EQ : TK_NE; jumpIfNull = SQLITE_NULLEQ; /* no break */ deliberate_fall_through case TK_LT: case TK_LE: case TK_GT: case TK_GE: case TK_NE: case TK_EQ: { if( sqlite3ExprIsVector(pExpr->pLeft) ) goto default_expr; |
︙ | ︙ | |||
104637 104638 104639 104640 104641 104642 104643 | } case TK_IS: case TK_ISNOT: testcase( pExpr->op==TK_IS ); testcase( pExpr->op==TK_ISNOT ); op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ; jumpIfNull = SQLITE_NULLEQ; | | | 104788 104789 104790 104791 104792 104793 104794 104795 104796 104797 104798 104799 104800 104801 104802 | } case TK_IS: case TK_ISNOT: testcase( pExpr->op==TK_IS ); testcase( pExpr->op==TK_ISNOT ); op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ; jumpIfNull = SQLITE_NULLEQ; /* no break */ deliberate_fall_through case TK_LT: case TK_LE: case TK_GT: case TK_GE: case TK_NE: case TK_EQ: { if( sqlite3ExprIsVector(pExpr->pLeft) ) goto default_expr; |
︙ | ︙ | |||
104949 104950 104951 104952 104953 104954 104955 | case TK_PLUS: case TK_MINUS: case TK_BITOR: case TK_LSHIFT: case TK_RSHIFT: case TK_CONCAT: seenNot = 1; | | | | 105100 105101 105102 105103 105104 105105 105106 105107 105108 105109 105110 105111 105112 105113 105114 105115 105116 105117 105118 105119 105120 | case TK_PLUS: case TK_MINUS: case TK_BITOR: case TK_LSHIFT: case TK_RSHIFT: case TK_CONCAT: seenNot = 1; /* no break */ deliberate_fall_through case TK_STAR: case TK_REM: case TK_BITAND: case TK_SLASH: { if( exprImpliesNotNull(pParse, p->pRight, pNN, iTab, seenNot) ) return 1; /* no break */ deliberate_fall_through } case TK_SPAN: case TK_COLLATE: case TK_UPLUS: case TK_UMINUS: { return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, seenNot); } |
︙ | ︙ | |||
105104 105105 105106 105107 105108 105109 105110 105111 105112 105113 105114 105115 105116 105117 | if( (pLeft->op==TK_COLUMN && ALWAYS(pLeft->y.pTab!=0) && IsVirtual(pLeft->y.pTab)) || (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0) && IsVirtual(pRight->y.pTab)) ){ return WRC_Prune; } } default: return WRC_Continue; } } /* | > | 105255 105256 105257 105258 105259 105260 105261 105262 105263 105264 105265 105266 105267 105268 105269 | if( (pLeft->op==TK_COLUMN && ALWAYS(pLeft->y.pTab!=0) && IsVirtual(pLeft->y.pTab)) || (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0) && IsVirtual(pRight->y.pTab)) ){ return WRC_Prune; } /* no break */ deliberate_fall_through } default: return WRC_Continue; } } /* |
︙ | ︙ | |||
106837 106838 106839 106840 106841 106842 106843 | sqlite3SelectPrep(pParse, pStep->pSelect, &sNC); if( pParse->nErr ) rc = pParse->rc; } if( rc==SQLITE_OK && pStep->zTarget ){ SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep); if( pSrc ){ int i; | | | 106989 106990 106991 106992 106993 106994 106995 106996 106997 106998 106999 107000 107001 107002 107003 | sqlite3SelectPrep(pParse, pStep->pSelect, &sNC); if( pParse->nErr ) rc = pParse->rc; } if( rc==SQLITE_OK && pStep->zTarget ){ SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep); if( pSrc ){ int i; for(i=0; i<pSrc->nSrc && rc==SQLITE_OK; i++){ struct SrcList_item *p = &pSrc->a[i]; p->pTab = sqlite3LocateTableItem(pParse, 0, p); p->iCursor = pParse->nTab++; if( p->pTab==0 ){ rc = SQLITE_ERROR; }else{ p->pTab->nTabRef++; |
︙ | ︙ | |||
107579 107580 107581 107582 107583 107584 107585 | #endif { "sqlite_stat3", 0 }, }; int i; sqlite3 *db = pParse->db; Db *pDb; Vdbe *v = sqlite3GetVdbe(pParse); | | | 107731 107732 107733 107734 107735 107736 107737 107738 107739 107740 107741 107742 107743 107744 107745 | #endif { "sqlite_stat3", 0 }, }; int i; sqlite3 *db = pParse->db; Db *pDb; Vdbe *v = sqlite3GetVdbe(pParse); u32 aRoot[ArraySize(aTable)]; u8 aCreateTbl[ArraySize(aTable)]; #ifdef SQLITE_ENABLE_STAT4 const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1; #else const int nToOpen = 1; #endif |
︙ | ︙ | |||
107608 107609 107610 107611 107612 107613 107614 | /* The sqlite_statN table does not exist. Create it. Note that a ** side-effect of the CREATE TABLE statement is to leave the rootpage ** of the new table in register pParse->regRoot. This is important ** because the OpenWrite opcode below will be needing it. */ sqlite3NestedParse(pParse, "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols ); | | | | | 107760 107761 107762 107763 107764 107765 107766 107767 107768 107769 107770 107771 107772 107773 107774 107775 107776 107777 107778 107779 107780 107781 107782 107783 107784 107785 107786 107787 107788 107789 107790 107791 107792 107793 107794 107795 107796 107797 107798 107799 107800 107801 107802 | /* The sqlite_statN table does not exist. Create it. Note that a ** side-effect of the CREATE TABLE statement is to leave the rootpage ** of the new table in register pParse->regRoot. This is important ** because the OpenWrite opcode below will be needing it. */ sqlite3NestedParse(pParse, "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols ); aRoot[i] = (u32)pParse->regRoot; aCreateTbl[i] = OPFLAG_P2ISREG; } }else{ /* The table already exists. If zWhere is not NULL, delete all entries ** associated with the table zWhere. If zWhere is NULL, delete the ** entire contents of the table. */ aRoot[i] = pStat->tnum; sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); if( zWhere ){ sqlite3NestedParse(pParse, "DELETE FROM %Q.%s WHERE %s=%Q", pDb->zDbSName, zTab, zWhereType, zWhere ); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK }else if( db->xPreUpdateCallback ){ sqlite3NestedParse(pParse, "DELETE FROM %Q.%s", pDb->zDbSName, zTab); #endif }else{ /* The sqlite_stat[134] table already exists. Delete all rows. */ sqlite3VdbeAddOp2(v, OP_Clear, (int)aRoot[i], iDb); } } } /* Open the sqlite_stat[134] tables for writing. */ for(i=0; i<nToOpen; i++){ assert( i<ArraySize(aTable) ); sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, (int)aRoot[i], iDb, 3); sqlite3VdbeChangeP5(v, aCreateTbl[i]); VdbeComment((v, aTable[i].zName)); } } /* ** Recommended number of samples for sqlite_stat4 |
︙ | ︙ | |||
110269 110270 110271 110272 110273 110274 110275 | #ifndef SQLITE_OMIT_SHARED_CACHE /* ** The TableLock structure is only used by the sqlite3TableLock() and ** codeTableLocks() functions. */ struct TableLock { int iDb; /* The database containing the table to be locked */ | | | | 110421 110422 110423 110424 110425 110426 110427 110428 110429 110430 110431 110432 110433 110434 110435 110436 110437 110438 110439 110440 110441 110442 110443 110444 110445 110446 110447 110448 110449 110450 110451 110452 110453 | #ifndef SQLITE_OMIT_SHARED_CACHE /* ** The TableLock structure is only used by the sqlite3TableLock() and ** codeTableLocks() functions. */ struct TableLock { int iDb; /* The database containing the table to be locked */ Pgno iTab; /* The root page of the table to be locked */ u8 isWriteLock; /* True for write lock. False for a read lock */ const char *zLockName; /* Name of the table */ }; /* ** Record the fact that we want to lock a table at run-time. ** ** The table to be locked has root page iTab and is found in database iDb. ** A read or a write lock can be taken depending on isWritelock. ** ** This routine just records the fact that the lock is desired. The ** code to make the lock occur is generated by a later call to ** codeTableLocks() which occurs during sqlite3FinishCoding(). */ SQLITE_PRIVATE void sqlite3TableLock( Parse *pParse, /* Parsing context */ int iDb, /* Index of the database containing the table to lock */ Pgno iTab, /* Root page number of the table to be locked */ u8 isWriteLock, /* True for a write lock */ const char *zName /* Name of the table to be locked */ ){ Parse *pToplevel = sqlite3ParseToplevel(pParse); int i; int nBytes; TableLock *p; |
︙ | ︙ | |||
111126 111127 111128 111129 111130 111131 111132 | SQLITE_PRIVATE int sqlite3CheckObjectName( Parse *pParse, /* Parsing context */ const char *zName, /* Name of the object to check */ const char *zType, /* Type of this object */ const char *zTblName /* Parent table name for triggers and indexes */ ){ sqlite3 *db = pParse->db; | | > > > < | | < | 111278 111279 111280 111281 111282 111283 111284 111285 111286 111287 111288 111289 111290 111291 111292 111293 111294 111295 111296 111297 111298 111299 111300 111301 111302 111303 111304 111305 | SQLITE_PRIVATE int sqlite3CheckObjectName( Parse *pParse, /* Parsing context */ const char *zName, /* Name of the object to check */ const char *zType, /* Type of this object */ const char *zTblName /* Parent table name for triggers and indexes */ ){ sqlite3 *db = pParse->db; if( sqlite3WritableSchema(db) || db->init.imposterTable || !sqlite3Config.bExtraSchemaChecks ){ /* Skip these error checks for writable_schema=ON */ return SQLITE_OK; } if( db->init.busy ){ if( sqlite3_stricmp(zType, db->init.azInit[0]) || sqlite3_stricmp(zName, db->init.azInit[1]) || sqlite3_stricmp(zTblName, db->init.azInit[2]) ){ sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */ return SQLITE_ERROR; } }else{ if( (pParse->nested==0 && 0==sqlite3StrNICmp(zName, "sqlite_", 7)) || (sqlite3ReadOnlyShadowTables(db) && sqlite3ShadowTableName(db, zName)) ){ sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName); |
︙ | ︙ | |||
112352 112353 112354 112355 112356 112357 112358 | /* Bypass the creation of the PRIMARY KEY btree and the sqlite_schema ** table entry. This is only required if currently generating VDBE ** code for a CREATE TABLE (not when parsing one as part of reading ** a database schema). */ if( v && pPk->tnum>0 ){ assert( db->init.busy==0 ); | | | 112505 112506 112507 112508 112509 112510 112511 112512 112513 112514 112515 112516 112517 112518 112519 | /* Bypass the creation of the PRIMARY KEY btree and the sqlite_schema ** table entry. This is only required if currently generating VDBE ** code for a CREATE TABLE (not when parsing one as part of reading ** a database schema). */ if( v && pPk->tnum>0 ){ assert( db->init.busy==0 ); sqlite3VdbeChangeOpcode(v, (int)pPk->tnum, OP_Goto); } /* The root page of the PRIMARY KEY is the table root page */ pPk->tnum = pTab->tnum; /* Update the in-memory representation of all UNIQUE indices by converting ** the final rowid column into one or more columns of the PRIMARY KEY. |
︙ | ︙ | |||
112940 112941 112942 112943 112944 112945 112946 | ** to the elements of the FROM clause. But we do not want these changes ** to be permanent. So the computation is done on a copy of the SELECT ** statement that defines the view. */ assert( pTable->pSelect ); pSel = sqlite3SelectDup(db, pTable->pSelect, 0); if( pSel ){ | < < | 113093 113094 113095 113096 113097 113098 113099 113100 113101 113102 113103 113104 113105 113106 113107 113108 | ** to the elements of the FROM clause. But we do not want these changes ** to be permanent. So the computation is done on a copy of the SELECT ** statement that defines the view. */ assert( pTable->pSelect ); pSel = sqlite3SelectDup(db, pTable->pSelect, 0); if( pSel ){ u8 eParseMode = pParse->eParseMode; pParse->eParseMode = PARSE_MODE_NORMAL; n = pParse->nTab; sqlite3SrcListAssignCursors(pParse, pSel->pSrc); pTable->nCol = -1; DisableLookaside; #ifndef SQLITE_OMIT_AUTHORIZATION xAuth = db->xAuth; db->xAuth = 0; |
︙ | ︙ | |||
112991 112992 112993 112994 112995 112996 112997 | pSelTab->aCol = 0; assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) ); } pTable->nNVCol = pTable->nCol; sqlite3DeleteTable(db, pSelTab); sqlite3SelectDelete(db, pSel); EnableLookaside; | < < | 113142 113143 113144 113145 113146 113147 113148 113149 113150 113151 113152 113153 113154 113155 113156 | pSelTab->aCol = 0; assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) ); } pTable->nNVCol = pTable->nCol; sqlite3DeleteTable(db, pSelTab); sqlite3SelectDelete(db, pSel); EnableLookaside; pParse->eParseMode = eParseMode; } else { nErr++; } pTable->pSchema->schemaFlags |= DB_UnresetViews; if( db->mallocFailed ){ sqlite3DeleteColumnNames(db, pTable); pTable->aCol = 0; |
︙ | ︙ | |||
113048 113049 113050 113051 113052 113053 113054 | ** because the first match might be for one of the deleted indices ** or tables and not the table/index that is actually being moved. ** We must continue looping until all tables and indices with ** rootpage==iFrom have been converted to have a rootpage of iTo ** in order to be certain that we got the right one. */ #ifndef SQLITE_OMIT_AUTOVACUUM | | | 113197 113198 113199 113200 113201 113202 113203 113204 113205 113206 113207 113208 113209 113210 113211 | ** because the first match might be for one of the deleted indices ** or tables and not the table/index that is actually being moved. ** We must continue looping until all tables and indices with ** rootpage==iFrom have been converted to have a rootpage of iTo ** in order to be certain that we got the right one. */ #ifndef SQLITE_OMIT_AUTOVACUUM SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, Pgno iFrom, Pgno iTo){ HashElem *pElem; Hash *pHash; Db *pDb; assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pDb = &db->aDb[iDb]; pHash = &pDb->pSchema->tblHash; |
︙ | ︙ | |||
113125 113126 113127 113128 113129 113130 113131 | ** OP_Destroy 5 0 ** ** and root page 5 happened to be the largest root-page number in the ** database, then root page 5 would be moved to page 4 by the ** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit ** a free-list page. */ | | | | | | 113274 113275 113276 113277 113278 113279 113280 113281 113282 113283 113284 113285 113286 113287 113288 113289 113290 113291 113292 113293 113294 113295 113296 113297 113298 113299 | ** OP_Destroy 5 0 ** ** and root page 5 happened to be the largest root-page number in the ** database, then root page 5 would be moved to page 4 by the ** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit ** a free-list page. */ Pgno iTab = pTab->tnum; Pgno iDestroyed = 0; while( 1 ){ Index *pIdx; Pgno iLargest = 0; if( iDestroyed==0 || iTab<iDestroyed ){ iLargest = iTab; } for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ Pgno iIdx = pIdx->tnum; assert( pIdx->pSchema==pTab->pSchema ); if( (iDestroyed==0 || (iIdx<iDestroyed)) && iIdx>iLargest ){ iLargest = iIdx; } } if( iLargest==0 ){ return; |
︙ | ︙ | |||
113559 113560 113561 113562 113563 113564 113565 | static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ Table *pTab = pIndex->pTable; /* The table that is indexed */ int iTab = pParse->nTab++; /* Btree cursor used for pTab */ int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */ int iSorter; /* Cursor opened by OpenSorter (if in use) */ int addr1; /* Address of top of loop */ int addr2; /* Address to jump to for next iteration */ | | | | 113708 113709 113710 113711 113712 113713 113714 113715 113716 113717 113718 113719 113720 113721 113722 113723 113724 113725 113726 113727 113728 113729 113730 113731 113732 113733 113734 113735 113736 113737 113738 113739 113740 113741 113742 113743 | static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ Table *pTab = pIndex->pTable; /* The table that is indexed */ int iTab = pParse->nTab++; /* Btree cursor used for pTab */ int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */ int iSorter; /* Cursor opened by OpenSorter (if in use) */ int addr1; /* Address of top of loop */ int addr2; /* Address to jump to for next iteration */ Pgno tnum; /* Root page of index */ int iPartIdxLabel; /* Jump to this label to skip a row */ Vdbe *v; /* Generate code into this virtual machine */ KeyInfo *pKey; /* KeyInfo for index */ int regRecord; /* Register holding assembled index record */ sqlite3 *db = pParse->db; /* The database connection */ int iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); #ifndef SQLITE_OMIT_AUTHORIZATION if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0, db->aDb[iDb].zDbSName ) ){ return; } #endif /* Require a write-lock on the table to perform this operation */ sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName); v = sqlite3GetVdbe(pParse); if( v==0 ) return; if( memRootPage>=0 ){ tnum = (Pgno)memRootPage; }else{ tnum = pIndex->tnum; } pKey = sqlite3KeyInfoOfIndex(pParse, pIndex); assert( pKey!=0 || db->mallocFailed || pParse->nErr ); /* Open the sorter cursor if we are to use one. */ |
︙ | ︙ | |||
113605 113606 113607 113608 113609 113610 113611 | sqlite3GenerateIndexKey(pParse,pIndex,iTab,regRecord,0,&iPartIdxLabel,0,0); sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord); sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr1); if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb); | | | 113754 113755 113756 113757 113758 113759 113760 113761 113762 113763 113764 113765 113766 113767 113768 | sqlite3GenerateIndexKey(pParse,pIndex,iTab,regRecord,0,&iPartIdxLabel,0,0); sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord); sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr1); if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb); sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, (int)tnum, iDb, (char *)pKey, P4_KEYINFO); sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0)); addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); VdbeCoverage(v); if( IsUniqueIndex(pIndex) ){ int j2 = sqlite3VdbeGoto(v, 1); addr2 = sqlite3VdbeCurrentAddr(v); |
︙ | ︙ | |||
114214 114215 114216 114217 114218 114219 114220 | /* Create the rootpage for the index using CreateIndex. But before ** doing so, code a Noop instruction and store its address in ** Index.tnum. This is required in case this index is actually a ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In ** that case the convertToWithoutRowidTable() routine will replace ** the Noop with a Goto to jump over the VDBE code generated below. */ | | | 114363 114364 114365 114366 114367 114368 114369 114370 114371 114372 114373 114374 114375 114376 114377 | /* Create the rootpage for the index using CreateIndex. But before ** doing so, code a Noop instruction and store its address in ** Index.tnum. This is required in case this index is actually a ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In ** that case the convertToWithoutRowidTable() routine will replace ** the Noop with a Goto to jump over the VDBE code generated below. */ pIndex->tnum = (Pgno)sqlite3VdbeAddOp0(v, OP_Noop); sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, iMem, BTREE_BLOBKEY); /* Gather the complete text of the CREATE INDEX statement into ** the zStmt variable */ assert( pName!=0 || pStart==0 ); if( pStart ){ |
︙ | ︙ | |||
114256 114257 114258 114259 114260 114261 114262 | sqlite3RefillIndex(pParse, pIndex, iMem); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddParseSchemaOp(v, iDb, sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); sqlite3VdbeAddOp2(v, OP_Expire, 0, 1); } | | | 114405 114406 114407 114408 114409 114410 114411 114412 114413 114414 114415 114416 114417 114418 114419 | sqlite3RefillIndex(pParse, pIndex, iMem); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddParseSchemaOp(v, iDb, sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); sqlite3VdbeAddOp2(v, OP_Expire, 0, 1); } sqlite3VdbeJumpHere(v, (int)pIndex->tnum); } } if( db->init.busy || pTblName==0 ){ pIndex->pNext = pTab->pIndex; pTab->pIndex = pIndex; pIndex = 0; } |
︙ | ︙ | |||
114331 114332 114333 114334 114335 114336 114337 | ** number of rows in the table, or half the number of rows in the table ** for a partial index. ** ** 2020-05-27: If some of the stat data is coming from the sqlite_stat1 ** table but other parts we are having to guess at, then do not let the ** estimated number of rows in the table be less than 1000 (LogEst 99). ** Failure to do this can cause the indexes for which we do not have | | | 114480 114481 114482 114483 114484 114485 114486 114487 114488 114489 114490 114491 114492 114493 114494 | ** number of rows in the table, or half the number of rows in the table ** for a partial index. ** ** 2020-05-27: If some of the stat data is coming from the sqlite_stat1 ** table but other parts we are having to guess at, then do not let the ** estimated number of rows in the table be less than 1000 (LogEst 99). ** Failure to do this can cause the indexes for which we do not have ** stat1 data to be ignored by the query planner. */ x = pIdx->pTable->nRowLogEst; assert( 99==sqlite3LogEst(1000) ); if( x<99 ){ pIdx->pTable->nRowLogEst = x = 99; } if( pIdx->pPartIdxWhere!=0 ) x -= 10; assert( 10==sqlite3LogEst(2) ); |
︙ | ︙ | |||
120307 120308 120309 120310 120311 120312 120313 120314 120315 120316 120317 120318 120319 120320 | pStep->op = TK_SELECT; break; case OE_Cascade: if( !pChanges ){ pStep->op = TK_DELETE; break; } default: pStep->op = TK_UPDATE; } pStep->pTrig = pTrigger; pTrigger->pSchema = pTab->pSchema; pTrigger->pTabSchema = pTab->pSchema; pFKey->apTrigger[iAction] = pTrigger; | > | 120456 120457 120458 120459 120460 120461 120462 120463 120464 120465 120466 120467 120468 120469 120470 | pStep->op = TK_SELECT; break; case OE_Cascade: if( !pChanges ){ pStep->op = TK_DELETE; break; } /* no break */ deliberate_fall_through default: pStep->op = TK_UPDATE; } pStep->pTrig = pTrigger; pTrigger->pSchema = pTab->pSchema; pTrigger->pTabSchema = pTab->pSchema; pFKey->apTrigger[iAction] = pTrigger; |
︙ | ︙ | |||
120579 120580 120581 120582 120583 120584 120585 | #endif for(i=1; i<iEnd; i++){ VdbeOp *pOp = sqlite3VdbeGetOp(v, i); assert( pOp!=0 ); if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){ Index *pIndex; | | | 120729 120730 120731 120732 120733 120734 120735 120736 120737 120738 120739 120740 120741 120742 120743 | #endif for(i=1; i<iEnd; i++){ VdbeOp *pOp = sqlite3VdbeGetOp(v, i); assert( pOp!=0 ); if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){ Index *pIndex; Pgno tnum = pOp->p2; if( tnum==pTab->tnum ){ return 1; } for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ if( tnum==pIndex->tnum ){ return 1; } |
︙ | ︙ | |||
122011 122012 122013 122014 122015 122016 122017 | nSeenReplace++; sqlite3ExprCodeCopy(pParse, pCol->pDflt, iReg); sqlite3VdbeJumpHere(v, addr1); break; } case OE_Abort: sqlite3MayAbort(pParse); | | | 122161 122162 122163 122164 122165 122166 122167 122168 122169 122170 122171 122172 122173 122174 122175 | nSeenReplace++; sqlite3ExprCodeCopy(pParse, pCol->pDflt, iReg); sqlite3VdbeJumpHere(v, addr1); break; } case OE_Abort: sqlite3MayAbort(pParse); /* no break */ deliberate_fall_through case OE_Rollback: case OE_Fail: { char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName, pCol->zName); sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, onError, iReg); sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC); |
︙ | ︙ | |||
122239 122240 122241 122242 122243 122244 122245 | sqlite3VdbeVerifyAbortable(v, onError); sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData); VdbeCoverage(v); switch( onError ){ default: { onError = OE_Abort; | | | 122389 122390 122391 122392 122393 122394 122395 122396 122397 122398 122399 122400 122401 122402 122403 | sqlite3VdbeVerifyAbortable(v, onError); sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData); VdbeCoverage(v); switch( onError ){ default: { onError = OE_Abort; /* no break */ deliberate_fall_through } case OE_Rollback: case OE_Abort: case OE_Fail: { testcase( onError==OE_Rollback ); testcase( onError==OE_Abort ); testcase( onError==OE_Fail ); |
︙ | ︙ | |||
122300 122301 122302 122303 122304 122305 122306 | } seenReplace = 1; break; } #ifndef SQLITE_OMIT_UPSERT case OE_Update: { sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, 0, iDataCur); | | | 122450 122451 122452 122453 122454 122455 122456 122457 122458 122459 122460 122461 122462 122463 122464 | } seenReplace = 1; break; } #ifndef SQLITE_OMIT_UPSERT case OE_Update: { sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, 0, iDataCur); /* no break */ deliberate_fall_through } #endif case OE_Ignore: { testcase( onError==OE_Ignore ); sqlite3VdbeGoto(v, ignoreDest); break; } |
︙ | ︙ | |||
122521 122522 122523 122524 122525 122526 122527 | testcase( onError==OE_Fail ); sqlite3UniqueConstraint(pParse, onError, pIdx); break; } #ifndef SQLITE_OMIT_UPSERT case OE_Update: { sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, pIdx, iIdxCur+ix); | | | 122671 122672 122673 122674 122675 122676 122677 122678 122679 122680 122681 122682 122683 122684 122685 | testcase( onError==OE_Fail ); sqlite3UniqueConstraint(pParse, onError, pIdx); break; } #ifndef SQLITE_OMIT_UPSERT case OE_Update: { sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, pIdx, iIdxCur+ix); /* no break */ deliberate_fall_through } #endif case OE_Ignore: { testcase( onError==OE_Ignore ); sqlite3VdbeGoto(v, ignoreDest); break; } |
︙ | ︙ | |||
126202 126203 126204 126205 126206 126207 126208 126209 126210 126211 126212 126213 | ** ** PRAGMA [schema.]page_count ** ** Return the number of pages in the specified database. */ case PragTyp_PAGE_COUNT: { int iReg; sqlite3CodeVerifySchema(pParse, iDb); iReg = ++pParse->nMem; if( sqlite3Tolower(zLeft[0])=='p' ){ sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg); }else{ | > > > > > > > | < | 126352 126353 126354 126355 126356 126357 126358 126359 126360 126361 126362 126363 126364 126365 126366 126367 126368 126369 126370 126371 126372 126373 126374 126375 126376 126377 126378 | ** ** PRAGMA [schema.]page_count ** ** Return the number of pages in the specified database. */ case PragTyp_PAGE_COUNT: { int iReg; i64 x = 0; sqlite3CodeVerifySchema(pParse, iDb); iReg = ++pParse->nMem; if( sqlite3Tolower(zLeft[0])=='p' ){ sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg); }else{ if( zRight && sqlite3DecOrHexToI64(zRight,&x)==0 ){ if( x<0 ) x = 0; else if( x>0xfffffffe ) x = 0xfffffffe; }else{ x = 0; } sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, (int)x); } sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1); break; } /* ** PRAGMA [schema.]locking_mode |
︙ | ︙ | |||
127111 127112 127113 127114 127115 127116 127117 127118 127119 127120 127121 127122 127123 127124 127125 127126 127127 | ** ** Verify the integrity of the database. ** ** The "quick_check" is reduced version of ** integrity_check designed to detect most database corruption ** without the overhead of cross-checking indexes. Quick_check ** is linear time wherease integrity_check is O(NlogN). */ case PragTyp_INTEGRITY_CHECK: { int i, j, addr, mxErr; int isQuick = (sqlite3Tolower(zLeft[0])=='q'); /* If the PRAGMA command was of the form "PRAGMA <db>.integrity_check", ** then iDb is set to the index of the database identified by <db>. ** In this case, the integrity of database iDb only is verified by ** the VDBE created below. | > > > > > > > > > > > > > | 127267 127268 127269 127270 127271 127272 127273 127274 127275 127276 127277 127278 127279 127280 127281 127282 127283 127284 127285 127286 127287 127288 127289 127290 127291 127292 127293 127294 127295 127296 | ** ** Verify the integrity of the database. ** ** The "quick_check" is reduced version of ** integrity_check designed to detect most database corruption ** without the overhead of cross-checking indexes. Quick_check ** is linear time wherease integrity_check is O(NlogN). ** ** The maximum nubmer of errors is 100 by default. A different default ** can be specified using a numeric parameter N. ** ** Or, the parameter N can be the name of a table. In that case, only ** the one table named is verified. The freelist is only verified if ** the named table is "sqlite_schema" (or one of its aliases). ** ** All schemas are checked by default. To check just a single ** schema, use the form: ** ** PRAGMA schema.integrity_check; */ case PragTyp_INTEGRITY_CHECK: { int i, j, addr, mxErr; Table *pObjTab = 0; /* Check only this one table, if not NULL */ int isQuick = (sqlite3Tolower(zLeft[0])=='q'); /* If the PRAGMA command was of the form "PRAGMA <db>.integrity_check", ** then iDb is set to the index of the database identified by <db>. ** In this case, the integrity of database iDb only is verified by ** the VDBE created below. |
︙ | ︙ | |||
127136 127137 127138 127139 127140 127141 127142 | /* Initialize the VDBE program */ pParse->nMem = 6; /* Set the maximum error count */ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; if( zRight ){ | | | | > > > > | 127305 127306 127307 127308 127309 127310 127311 127312 127313 127314 127315 127316 127317 127318 127319 127320 127321 127322 127323 127324 127325 | /* Initialize the VDBE program */ pParse->nMem = 6; /* Set the maximum error count */ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; if( zRight ){ if( sqlite3GetInt32(zRight, &mxErr) ){ if( mxErr<=0 ){ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; } }else{ pObjTab = sqlite3LocateTable(pParse, 0, zRight, iDb>=0 ? db->aDb[iDb].zDbSName : 0); } } sqlite3VdbeAddOp2(v, OP_Integer, mxErr-1, 1); /* reg[1] holds errors left */ /* Do an integrity check on each database file */ for(i=0; i<db->nDb; i++){ HashElem *x; /* For looping over tables in the schema */ |
︙ | ︙ | |||
127167 127168 127169 127170 127171 127172 127173 127174 127175 127176 127177 127178 127179 | */ assert( sqlite3SchemaMutexHeld(db, i, 0) ); pTbls = &db->aDb[i].pSchema->tblHash; for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); /* Current table */ Index *pIdx; /* An index on pTab */ int nIdx; /* Number of indexes on pTab */ if( HasRowid(pTab) ) cnt++; for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; } if( nIdx>mxIdx ) mxIdx = nIdx; } aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1)); if( aRoot==0 ) break; | > > > > > | > | 127340 127341 127342 127343 127344 127345 127346 127347 127348 127349 127350 127351 127352 127353 127354 127355 127356 127357 127358 127359 127360 127361 127362 127363 127364 127365 127366 127367 127368 | */ assert( sqlite3SchemaMutexHeld(db, i, 0) ); pTbls = &db->aDb[i].pSchema->tblHash; for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); /* Current table */ Index *pIdx; /* An index on pTab */ int nIdx; /* Number of indexes on pTab */ if( pObjTab && pObjTab!=pTab ) continue; if( HasRowid(pTab) ) cnt++; for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; } if( nIdx>mxIdx ) mxIdx = nIdx; } if( cnt==0 ) continue; if( pObjTab ) cnt++; aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1)); if( aRoot==0 ) break; cnt = 0; if( pObjTab ) aRoot[++cnt] = 0; for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); Index *pIdx; if( pObjTab && pObjTab!=pTab ) continue; if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ aRoot[++cnt] = pIdx->tnum; } } aRoot[0] = cnt; |
︙ | ︙ | |||
127209 127210 127211 127212 127213 127214 127215 127216 127217 127218 127219 127220 127221 127222 | Index *pIdx, *pPk; Index *pPrior = 0; int loopTop; int iDataCur, iIdxCur; int r1 = -1; if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */ pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0, 1, 0, &iDataCur, &iIdxCur); /* reg[7] counts the number of entries in the table. ** reg[8+i] counts the number of entries in the i-th index */ sqlite3VdbeAddOp2(v, OP_Integer, 0, 7); | > | 127388 127389 127390 127391 127392 127393 127394 127395 127396 127397 127398 127399 127400 127401 127402 | Index *pIdx, *pPk; Index *pPrior = 0; int loopTop; int iDataCur, iIdxCur; int r1 = -1; if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */ if( pObjTab && pObjTab!=pTab ) continue; pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0, 1, 0, &iDataCur, &iIdxCur); /* reg[7] counts the number of entries in the table. ** reg[8+i] counts the number of entries in the i-th index */ sqlite3VdbeAddOp2(v, OP_Integer, 0, 7); |
︙ | ︙ | |||
128270 128271 128272 128273 128274 128275 128276 | int rc; u8 saved_iDb = db->init.iDb; sqlite3_stmt *pStmt; TESTONLY(int rcp); /* Return code from sqlite3_prepare() */ assert( db->init.busy ); db->init.iDb = iDb; | | > > > > > > | 128450 128451 128452 128453 128454 128455 128456 128457 128458 128459 128460 128461 128462 128463 128464 128465 128466 128467 128468 128469 128470 | int rc; u8 saved_iDb = db->init.iDb; sqlite3_stmt *pStmt; TESTONLY(int rcp); /* Return code from sqlite3_prepare() */ assert( db->init.busy ); db->init.iDb = iDb; if( sqlite3GetUInt32(argv[3], &db->init.newTnum)==0 || (db->init.newTnum>pData->mxPage && pData->mxPage>0) ){ if( sqlite3Config.bExtraSchemaChecks ){ corruptSchema(pData, argv[1], "invalid rootpage"); } } db->init.orphanTrigger = 0; db->init.azInit = argv; pStmt = 0; TESTONLY(rcp = ) sqlite3Prepare(db, argv[4], -1, 0, 0, &pStmt, 0); rc = db->errCode; assert( (rc&0xFF)==(rcp&0xFF) ); db->init.iDb = saved_iDb; |
︙ | ︙ | |||
128303 128304 128305 128306 128307 128308 128309 | ** was created to be the PRIMARY KEY or to fulfill a UNIQUE ** constraint for a CREATE TABLE. The index should have already ** been created when we processed the CREATE TABLE. All we have ** to do here is record the root page number for that index. */ Index *pIndex; pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName); | | > > | > > | > | 128489 128490 128491 128492 128493 128494 128495 128496 128497 128498 128499 128500 128501 128502 128503 128504 128505 128506 128507 128508 128509 128510 128511 128512 128513 | ** was created to be the PRIMARY KEY or to fulfill a UNIQUE ** constraint for a CREATE TABLE. The index should have already ** been created when we processed the CREATE TABLE. All we have ** to do here is record the root page number for that index. */ Index *pIndex; pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName); if( pIndex==0 ){ corruptSchema(pData, argv[1], "orphan index"); }else if( sqlite3GetUInt32(argv[3],&pIndex->tnum)==0 || pIndex->tnum<2 || pIndex->tnum>pData->mxPage || sqlite3IndexHasDuplicateRootPage(pIndex) ){ if( sqlite3Config.bExtraSchemaChecks ){ corruptSchema(pData, argv[1], "invalid rootpage"); } } } return 0; } /* ** Attempt to read the database schema and initialize internal |
︙ | ︙ | |||
128362 128363 128364 128365 128366 128367 128368 128369 128370 128371 128372 128373 128374 128375 | azArg[5] = 0; initData.db = db; initData.iDb = iDb; initData.rc = SQLITE_OK; initData.pzErrMsg = pzErrMsg; initData.mInitFlags = mFlags; initData.nInitRow = 0; sqlite3InitCallback(&initData, 5, (char **)azArg, 0); db->mDbFlags &= mask; if( initData.rc ){ rc = initData.rc; goto error_out; } | > | 128553 128554 128555 128556 128557 128558 128559 128560 128561 128562 128563 128564 128565 128566 128567 | azArg[5] = 0; initData.db = db; initData.iDb = iDb; initData.rc = SQLITE_OK; initData.pzErrMsg = pzErrMsg; initData.mInitFlags = mFlags; initData.nInitRow = 0; initData.mxPage = 0; sqlite3InitCallback(&initData, 5, (char **)azArg, 0); db->mDbFlags &= mask; if( initData.rc ){ rc = initData.rc; goto error_out; } |
︙ | ︙ | |||
128484 128485 128486 128487 128488 128489 128490 128491 128492 128493 128494 128495 128496 128497 | if( iDb==0 && meta[BTREE_FILE_FORMAT-1]>=4 ){ db->flags &= ~(u64)SQLITE_LegacyFileFmt; } /* Read the schema information out of the schema tables */ assert( db->init.busy ); { char *zSql; zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\".%s ORDER BY rowid", db->aDb[iDb].zDbSName, zSchemaTabName); #ifndef SQLITE_OMIT_AUTHORIZATION { | > | 128676 128677 128678 128679 128680 128681 128682 128683 128684 128685 128686 128687 128688 128689 128690 | if( iDb==0 && meta[BTREE_FILE_FORMAT-1]>=4 ){ db->flags &= ~(u64)SQLITE_LegacyFileFmt; } /* Read the schema information out of the schema tables */ assert( db->init.busy ); initData.mxPage = sqlite3BtreeLastPage(pDb->pBt); { char *zSql; zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\".%s ORDER BY rowid", db->aDb[iDb].zDbSName, zSchemaTabName); #ifndef SQLITE_OMIT_AUTHORIZATION { |
︙ | ︙ | |||
129366 129367 129368 129369 129370 129371 129372 | /* ** Return the index of a column in a table. Return -1 if the column ** is not contained in the table. */ static int columnIndex(Table *pTab, const char *zCol){ int i; | > > | | | 129559 129560 129561 129562 129563 129564 129565 129566 129567 129568 129569 129570 129571 129572 129573 129574 129575 129576 | /* ** Return the index of a column in a table. Return -1 if the column ** is not contained in the table. */ static int columnIndex(Table *pTab, const char *zCol){ int i; u8 h = sqlite3StrIHash(zCol); Column *pCol; for(pCol=pTab->aCol, i=0; i<pTab->nCol; pCol++, i++){ if( pCol->hName==h && sqlite3StrICmp(pCol->zName, zCol)==0 ) return i; } return -1; } /* ** Search the first N tables in pSrc, from left to right, looking for a ** table that has a column named zCol. |
︙ | ︙ | |||
130229 130230 130231 130232 130233 130234 130235 | sqlite3ReleaseTempReg(pParse, r2); } sqlite3ReleaseTempRange(pParse, r1, nPrefixReg+1); break; } case SRT_Upfrom: { | < | < < > > > > > > | > | 130424 130425 130426 130427 130428 130429 130430 130431 130432 130433 130434 130435 130436 130437 130438 130439 130440 130441 130442 130443 130444 130445 130446 130447 130448 130449 130450 130451 | sqlite3ReleaseTempReg(pParse, r2); } sqlite3ReleaseTempRange(pParse, r1, nPrefixReg+1); break; } case SRT_Upfrom: { if( pSort ){ pushOntoSorter( pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); }else{ int i2 = pDest->iSDParm2; int r1 = sqlite3GetTempReg(pParse); /* If the UPDATE FROM join is an aggregate that matches no rows, it ** might still be trying to return one row, because that is what ** aggregates do. Don't record that empty row in the output table. */ sqlite3VdbeAddOp2(v, OP_IsNull, regResult, iBreak); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult+(i2<0), nResultCol-(i2<0), r1); if( i2<0 ){ sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regResult); }else{ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, i2); } } break; |
︙ | ︙ | |||
130680 130681 130682 130683 130684 130685 130686 | break; } case SRT_Mem: { /* The LIMIT clause will terminate the loop for us */ break; } #endif | < < | 130879 130880 130881 130882 130883 130884 130885 130886 130887 130888 130889 130890 130891 130892 130893 130894 130895 130896 130897 130898 130899 130900 130901 130902 130903 | break; } case SRT_Mem: { /* The LIMIT clause will terminate the loop for us */ break; } #endif case SRT_Upfrom: { int i2 = pDest->iSDParm2; int r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_MakeRecord,regRow+(i2<0),nColumn-(i2<0),r1); if( i2<0 ){ sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regRow); }else{ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regRow, i2); } break; } default: { assert( eDest==SRT_Output || eDest==SRT_Coroutine ); testcase( eDest==SRT_Output ); testcase( eDest==SRT_Coroutine ); if( eDest==SRT_Output ){ sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iSdst, nColumn); }else{ |
︙ | ︙ | |||
132279 132280 132281 132282 132283 132284 132285 | int addr1; /* Jump instructions that get retargetted */ int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */ KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ KeyInfo *pKeyMerge; /* Comparison information for merging rows */ sqlite3 *db; /* Database connection */ ExprList *pOrderBy; /* The ORDER BY clause */ int nOrderBy; /* Number of terms in the ORDER BY clause */ | | | 132476 132477 132478 132479 132480 132481 132482 132483 132484 132485 132486 132487 132488 132489 132490 | int addr1; /* Jump instructions that get retargetted */ int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */ KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ KeyInfo *pKeyMerge; /* Comparison information for merging rows */ sqlite3 *db; /* Database connection */ ExprList *pOrderBy; /* The ORDER BY clause */ int nOrderBy; /* Number of terms in the ORDER BY clause */ u32 *aPermute; /* Mapping from ORDER BY terms to result set columns */ assert( p->pOrderBy!=0 ); assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */ db = pParse->db; v = pParse->pVdbe; assert( v!=0 ); /* Already thrown the error if VDBE alloc failed */ labelEnd = sqlite3VdbeMakeLabel(pParse); |
︙ | ︙ | |||
132328 132329 132330 132331 132332 132333 132334 | /* Compute the comparison permutation and keyinfo that is used with ** the permutation used to determine if the next ** row of results comes from selectA or selectB. Also add explicit ** collations to the ORDER BY clause terms so that when the subqueries ** to the right and the left are evaluated, they use the correct ** collation. */ | | | 132525 132526 132527 132528 132529 132530 132531 132532 132533 132534 132535 132536 132537 132538 132539 | /* Compute the comparison permutation and keyinfo that is used with ** the permutation used to determine if the next ** row of results comes from selectA or selectB. Also add explicit ** collations to the ORDER BY clause terms so that when the subqueries ** to the right and the left are evaluated, they use the correct ** collation. */ aPermute = sqlite3DbMallocRawNN(db, sizeof(u32)*(nOrderBy + 1)); if( aPermute ){ struct ExprList_item *pItem; aPermute[0] = nOrderBy; for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){ assert( pItem->u.x.iOrderByCol>0 ); assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr ); aPermute[i] = pItem->u.x.iOrderByCol - 1; |
︙ | ︙ | |||
133284 133285 133286 133287 133288 133289 133290 | ** success. */ sqlite3AggInfoPersistWalkerInit(&w, pParse); sqlite3WalkSelect(&w,pSub1); sqlite3SelectDelete(db, pSub1); #if SELECTTRACE_ENABLED | | | 133481 133482 133483 133484 133485 133486 133487 133488 133489 133490 133491 133492 133493 133494 133495 | ** success. */ sqlite3AggInfoPersistWalkerInit(&w, pParse); sqlite3WalkSelect(&w,pSub1); sqlite3SelectDelete(db, pSub1); #if SELECTTRACE_ENABLED if( sqlite3_unsupported_selecttrace & 0x100 ){ SELECTTRACE(0x100,pParse,p,("After flattening:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif return 1; } |
︙ | ︙ | |||
134722 134723 134724 134725 134726 134727 134728 | Walker sWalker; memset(&sWalker, 0, sizeof(sWalker)); sWalker.pParse = pParse; sWalker.xExprCallback = havingToWhereExprCb; sWalker.u.pSelect = p; sqlite3WalkExpr(&sWalker, p->pHaving); #if SELECTTRACE_ENABLED | | | 134919 134920 134921 134922 134923 134924 134925 134926 134927 134928 134929 134930 134931 134932 134933 | Walker sWalker; memset(&sWalker, 0, sizeof(sWalker)); sWalker.pParse = pParse; sWalker.xExprCallback = havingToWhereExprCb; sWalker.u.pSelect = p; sqlite3WalkExpr(&sWalker, p->pHaving); #if SELECTTRACE_ENABLED if( sWalker.eCode && (sqlite3_unsupported_selecttrace & 0x100)!=0 ){ SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif } /* |
︙ | ︙ | |||
134844 134845 134846 134847 134848 134849 134850 | } pSub = pPrior; } p->pEList->a[0].pExpr = pExpr; p->selFlags &= ~SF_Aggregate; #if SELECTTRACE_ENABLED | | | 135041 135042 135043 135044 135045 135046 135047 135048 135049 135050 135051 135052 135053 135054 135055 | } pSub = pPrior; } p->pEList->a[0].pExpr = pExpr; p->selFlags &= ~SF_Aggregate; #if SELECTTRACE_ENABLED if( sqlite3_unsupported_selecttrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif return 1; } #endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */ |
︙ | ︙ | |||
134897 134898 134899 134900 134901 134902 134903 | v = sqlite3GetVdbe(pParse); if( p==0 || db->mallocFailed || pParse->nErr ){ return 1; } if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; #if SELECTTRACE_ENABLED SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain)); | | | 135094 135095 135096 135097 135098 135099 135100 135101 135102 135103 135104 135105 135106 135107 135108 | v = sqlite3GetVdbe(pParse); if( p==0 || db->mallocFailed || pParse->nErr ){ return 1; } if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; #if SELECTTRACE_ENABLED SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain)); if( sqlite3_unsupported_selecttrace & 0x100 ){ sqlite3TreeViewSelect(0, p, 0); } #endif assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo ); assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo ); assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue ); |
︙ | ︙ | |||
134924 134925 134926 134927 134928 134929 134930 | } sqlite3SelectPrep(pParse, p, 0); if( pParse->nErr || db->mallocFailed ){ goto select_end; } assert( p->pEList!=0 ); #if SELECTTRACE_ENABLED | | | 135121 135122 135123 135124 135125 135126 135127 135128 135129 135130 135131 135132 135133 135134 135135 | } sqlite3SelectPrep(pParse, p, 0); if( pParse->nErr || db->mallocFailed ){ goto select_end; } assert( p->pEList!=0 ); #if SELECTTRACE_ENABLED if( sqlite3_unsupported_selecttrace & 0x104 ){ SELECTTRACE(0x104,pParse,p, ("after name resolution:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif /* If the SF_UpdateFrom flag is set, then this function is being called ** as part of populating the temp table for an UPDATE...FROM statement. |
︙ | ︙ | |||
134959 134960 134961 134962 134963 134964 134965 | #ifndef SQLITE_OMIT_WINDOWFUNC rc = sqlite3WindowRewrite(pParse, p); if( rc ){ assert( db->mallocFailed || pParse->nErr>0 ); goto select_end; } #if SELECTTRACE_ENABLED | | | 135156 135157 135158 135159 135160 135161 135162 135163 135164 135165 135166 135167 135168 135169 135170 | #ifndef SQLITE_OMIT_WINDOWFUNC rc = sqlite3WindowRewrite(pParse, p); if( rc ){ assert( db->mallocFailed || pParse->nErr>0 ); goto select_end; } #if SELECTTRACE_ENABLED if( p->pWin && (sqlite3_unsupported_selecttrace & 0x108)!=0 ){ SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif #endif /* SQLITE_OMIT_WINDOWFUNC */ pTabList = p->pSrc; isAgg = (p->selFlags & SF_Aggregate)!=0; |
︙ | ︙ | |||
135066 135067 135068 135069 135070 135071 135072 | /* Handle compound SELECT statements using the separate multiSelect() ** procedure. */ if( p->pPrior ){ rc = multiSelect(pParse, p, pDest); #if SELECTTRACE_ENABLED SELECTTRACE(0x1,pParse,p,("end compound-select processing\n")); | | | | 135263 135264 135265 135266 135267 135268 135269 135270 135271 135272 135273 135274 135275 135276 135277 135278 135279 135280 135281 135282 135283 135284 135285 135286 135287 135288 135289 135290 135291 135292 135293 135294 135295 135296 | /* Handle compound SELECT statements using the separate multiSelect() ** procedure. */ if( p->pPrior ){ rc = multiSelect(pParse, p, pDest); #if SELECTTRACE_ENABLED SELECTTRACE(0x1,pParse,p,("end compound-select processing\n")); if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ sqlite3TreeViewSelect(0, p, 0); } #endif if( p->pNext==0 ) ExplainQueryPlanPop(pParse); return rc; } #endif /* Do the WHERE-clause constant propagation optimization if this is ** a join. No need to speed time on this operation for non-join queries ** as the equivalent optimization will be handled by query planner in ** sqlite3WhereBegin(). */ if( pTabList->nSrc>1 && OptimizationEnabled(db, SQLITE_PropagateConst) && propagateConstants(pParse, p) ){ #if SELECTTRACE_ENABLED if( sqlite3_unsupported_selecttrace & 0x100 ){ SELECTTRACE(0x100,pParse,p,("After constant propagation:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif }else{ SELECTTRACE(0x100,pParse,p,("Constant propagation not helpful\n")); } |
︙ | ︙ | |||
135173 135174 135175 135176 135177 135178 135179 | ** inside the subquery. This can help the subquery to run more efficiently. */ if( OptimizationEnabled(db, SQLITE_PushDown) && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor, (pItem->fg.jointype & JT_OUTER)!=0) ){ #if SELECTTRACE_ENABLED | | | 135370 135371 135372 135373 135374 135375 135376 135377 135378 135379 135380 135381 135382 135383 135384 | ** inside the subquery. This can help the subquery to run more efficiently. */ if( OptimizationEnabled(db, SQLITE_PushDown) && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor, (pItem->fg.jointype & JT_OUTER)!=0) ){ #if SELECTTRACE_ENABLED if( sqlite3_unsupported_selecttrace & 0x100 ){ SELECTTRACE(0x100,pParse,p, ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); sqlite3TreeViewSelect(0, p, 0); } #endif }else{ SELECTTRACE(0x100,pParse,p,("Push-down not possible\n")); |
︙ | ︙ | |||
135273 135274 135275 135276 135277 135278 135279 | pEList = p->pEList; pWhere = p->pWhere; pGroupBy = p->pGroupBy; pHaving = p->pHaving; sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; #if SELECTTRACE_ENABLED | | | 135470 135471 135472 135473 135474 135475 135476 135477 135478 135479 135480 135481 135482 135483 135484 | pEList = p->pEList; pWhere = p->pWhere; pGroupBy = p->pGroupBy; pHaving = p->pHaving; sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; #if SELECTTRACE_ENABLED if( sqlite3_unsupported_selecttrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and ** if the select-list is the same as the ORDER BY list, then this query |
︙ | ︙ | |||
135309 135310 135311 135312 135313 135314 135315 | p->selFlags |= SF_Aggregate; /* Notice that even thought SF_Distinct has been cleared from p->selFlags, ** the sDistinct.isTnct is still set. Hence, isTnct represents the ** original setting of the SF_Distinct flag, not the current setting */ assert( sDistinct.isTnct ); #if SELECTTRACE_ENABLED | | | 135506 135507 135508 135509 135510 135511 135512 135513 135514 135515 135516 135517 135518 135519 135520 | p->selFlags |= SF_Aggregate; /* Notice that even thought SF_Distinct has been cleared from p->selFlags, ** the sDistinct.isTnct is still set. Hence, isTnct represents the ** original setting of the SF_Distinct flag, not the current setting */ assert( sDistinct.isTnct ); #if SELECTTRACE_ENABLED if( sqlite3_unsupported_selecttrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif } /* If there is an ORDER BY clause, then create an ephemeral index to |
︙ | ︙ | |||
135557 135558 135559 135560 135561 135562 135563 | } #endif sNC.ncFlags &= ~NC_InAggFunc; } pAggInfo->mxReg = pParse->nMem; if( db->mallocFailed ) goto select_end; #if SELECTTRACE_ENABLED | | | 135754 135755 135756 135757 135758 135759 135760 135761 135762 135763 135764 135765 135766 135767 135768 | } #endif sNC.ncFlags &= ~NC_InAggFunc; } pAggInfo->mxReg = pParse->nMem; if( db->mallocFailed ) goto select_end; #if SELECTTRACE_ENABLED if( sqlite3_unsupported_selecttrace & 0x400 ){ int ii; SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); sqlite3TreeViewSelect(0, p, 0); for(ii=0; ii<pAggInfo->nColumn; ii++){ sqlite3DebugPrintf("agg-column[%d] iMem=%d\n", ii, pAggInfo->aCol[ii].iMem); sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0); |
︙ | ︙ | |||
135821 135822 135823 135824 135825 135826 135827 | ** always spread across less pages than their corresponding tables. */ const int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); const int iCsr = pParse->nTab++; /* Cursor to scan b-tree */ Index *pIdx; /* Iterator variable */ KeyInfo *pKeyInfo = 0; /* Keyinfo for scanned index */ Index *pBest = 0; /* Best index found so far */ | | | 136018 136019 136020 136021 136022 136023 136024 136025 136026 136027 136028 136029 136030 136031 136032 | ** always spread across less pages than their corresponding tables. */ const int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); const int iCsr = pParse->nTab++; /* Cursor to scan b-tree */ Index *pIdx; /* Iterator variable */ KeyInfo *pKeyInfo = 0; /* Keyinfo for scanned index */ Index *pBest = 0; /* Best index found so far */ Pgno iRoot = pTab->tnum; /* Root page of scanned b-tree */ sqlite3CodeVerifySchema(pParse, iDb); sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); /* Search for the index that has the lowest scan cost. ** ** (2011-04-15) Do not do a full scan of an unordered index. |
︙ | ︙ | |||
135853 135854 135855 135856 135857 135858 135859 | } if( pBest ){ iRoot = pBest->tnum; pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest); } /* Open a read-only cursor, execute the OP_Count, close the cursor. */ | | | 136050 136051 136052 136053 136054 136055 136056 136057 136058 136059 136060 136061 136062 136063 136064 | } if( pBest ){ iRoot = pBest->tnum; pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest); } /* Open a read-only cursor, execute the OP_Count, close the cursor. */ sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, (int)iRoot, iDb, 1); if( pKeyInfo ){ sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); } sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem); sqlite3VdbeAddOp1(v, OP_Close, iCsr); explainSimpleCount(pParse, pTab, pBest); }else{ |
︙ | ︙ | |||
135976 135977 135978 135979 135980 135981 135982 | assert( pExpr->iAgg==i ); } } #endif #if SELECTTRACE_ENABLED SELECTTRACE(0x1,pParse,p,("end processing\n")); | | | 136173 136174 136175 136176 136177 136178 136179 136180 136181 136182 136183 136184 136185 136186 136187 | assert( pExpr->iAgg==i ); } } #endif #if SELECTTRACE_ENABLED SELECTTRACE(0x1,pParse,p,("end processing\n")); if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ sqlite3TreeViewSelect(0, p, 0); } #endif ExplainQueryPlanPop(pParse); return rc; } |
︙ | ︙ | |||
142373 142374 142375 142376 142377 142378 142379 | pWInfo->bDeferredSeek = 1; sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur); if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) && DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask) ){ int i; Table *pTab = pIdx->pTable; | | | 142570 142571 142572 142573 142574 142575 142576 142577 142578 142579 142580 142581 142582 142583 142584 | pWInfo->bDeferredSeek = 1; sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur); if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) && DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask) ){ int i; Table *pTab = pIdx->pTable; u32 *ai = (u32*)sqlite3DbMallocZero(pParse->db, sizeof(u32)*(pTab->nCol+1)); if( ai ){ ai[0] = pTab->nCol; for(i=0; i<pIdx->nColumn-1; i++){ int x1, x2; assert( pIdx->aiColumn[i]<pTab->nCol ); x1 = pIdx->aiColumn[i]; x2 = sqlite3TableColumnToStorage(pTab, x1); |
︙ | ︙ | |||
151747 151748 151749 151750 151751 151752 151753 | for(pWin=p->pWin; pWin; pWin=pWin->pNextWin){ if( pExpr->y.pWin==pWin ){ assert( pWin->pOwner==pExpr ); return WRC_Prune; } } } | | | 151944 151945 151946 151947 151948 151949 151950 151951 151952 151953 151954 151955 151956 151957 151958 | for(pWin=p->pWin; pWin; pWin=pWin->pNextWin){ if( pExpr->y.pWin==pWin ){ assert( pWin->pOwner==pExpr ); return WRC_Prune; } } } /* no break */ deliberate_fall_through case TK_AGG_FUNCTION: case TK_COLUMN: { int iCol = -1; if( p->pSub ){ int i; for(i=0; i<p->pSub->nExpr; i++){ |
︙ | ︙ | |||
159964 159965 159966 159967 159968 159969 159970 159971 159972 159973 159974 159975 159976 159977 | #endif { *tokenType = TK_DOT; return 1; } /* If the next character is a digit, this is a floating point ** number that begins with ".". Fall thru into the next case */ } case CC_DIGIT: { testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' ); testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); testcase( z[0]=='9' ); *tokenType = TK_INTEGER; | > | 160161 160162 160163 160164 160165 160166 160167 160168 160169 160170 160171 160172 160173 160174 160175 | #endif { *tokenType = TK_DOT; return 1; } /* If the next character is a digit, this is a floating point ** number that begins with ".". Fall thru into the next case */ /* no break */ deliberate_fall_through } case CC_DIGIT: { testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' ); testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); testcase( z[0]=='9' ); *tokenType = TK_INTEGER; |
︙ | ︙ | |||
160068 160069 160070 160071 160072 160073 160074 160075 160076 160077 160078 160079 160080 160081 | } if( z[i] ) i++; return i; } #endif /* If it is not a BLOB literal, then it must be an ID, since no ** SQL keywords start with the letter 'x'. Fall through */ } case CC_ID: { i = 1; break; } case CC_NUL: { *tokenType = TK_ILLEGAL; | > | 160266 160267 160268 160269 160270 160271 160272 160273 160274 160275 160276 160277 160278 160279 160280 | } if( z[i] ) i++; return i; } #endif /* If it is not a BLOB literal, then it must be an ID, since no ** SQL keywords start with the letter 'x'. Fall through */ /* no break */ deliberate_fall_through } case CC_ID: { i = 1; break; } case CC_NUL: { *tokenType = TK_ILLEGAL; |
︙ | ︙ | |||
161998 161999 162000 162001 162002 162003 162004 | return SQLITE_OK; } if( !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); if( db->mTrace & SQLITE_TRACE_CLOSE ){ | | | 162197 162198 162199 162200 162201 162202 162203 162204 162205 162206 162207 162208 162209 162210 162211 | return SQLITE_OK; } if( !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); if( db->mTrace & SQLITE_TRACE_CLOSE ){ db->trace.xV2(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0); } /* Force xDisconnect calls on all virtual tables */ disconnectAllVtab(db); /* If a transaction is open, the disconnectAllVtab() call above ** will not have called the xDisconnect() method on any virtual |
︙ | ︙ | |||
162887 162888 162889 162890 162891 162892 162893 | (void)SQLITE_MISUSE_BKPT; return 0; } #endif sqlite3_mutex_enter(db->mutex); pOld = db->pTraceArg; db->mTrace = xTrace ? SQLITE_TRACE_LEGACY : 0; |