Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Another merge from trunk. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | ttmrichter-skins |
| Files: | files | file ages | folders |
| SHA1: |
1516a26dc85d4758620fbc77ffc4984d |
| User & Date: | michael 2010-09-16 14:13:51.000 |
Context
|
2010-09-24
| ||
| 17:07 | Merging from trunk. check-in: e3292ef8c3 user: michael tags: ttmrichter-skins | |
|
2010-09-16
| ||
| 14:13 | Another merge from trunk. check-in: 1516a26dc8 user: michael tags: ttmrichter-skins | |
| 14:00 | Committing a merge. check-in: be5fd10aaf user: michael tags: ttmrichter-skins | |
|
2010-09-13
| ||
| 23:40 | Merge the CSS and XHTML fixes from the wolfgangFormat2CSS branch into the trunk. check-in: 3243e63bba user: drh tags: trunk | |
Changes
Changes to src/add.c.
| ︙ | ︙ | |||
51 52 53 54 55 56 57 |
|| blob_compare(&pathname, pOmit)==0
){
fossil_warning("cannot add %s", zPath);
}else{
if( !file_is_simple_pathname(zPath) ){
fossil_fatal("filename contains illegal characters: %s", zPath);
}
| | | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
|| blob_compare(&pathname, pOmit)==0
){
fossil_warning("cannot add %s", zPath);
}else{
if( !file_is_simple_pathname(zPath) ){
fossil_fatal("filename contains illegal characters: %s", zPath);
}
#if defined(_WIN32)
if( db_exists("SELECT 1 FROM vfile"
" WHERE pathname=%Q COLLATE nocase", zPath) ){
db_multi_exec("UPDATE vfile SET deleted=0"
" WHERE pathname=%Q COLLATE nocase", zPath);
}
#else
if( db_exists("SELECT 1 FROM vfile WHERE pathname=%Q", zPath) ){
|
| ︙ | ︙ | |||
150 151 152 153 154 155 156 |
fossil_panic("no checkout to add to");
}
db_begin_transaction();
if( !file_tree_name(g.zRepositoryName, &repo, 0) ){
blob_zero(&repo);
}
db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
| | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
fossil_panic("no checkout to add to");
}
db_begin_transaction();
if( !file_tree_name(g.zRepositoryName, &repo, 0) ){
blob_zero(&repo);
}
db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
#if defined(_WIN32)
db_multi_exec(
"CREATE INDEX IF NOT EXISTS vfile_pathname "
" ON vfile(pathname COLLATE nocase)"
);
#endif
for(i=2; i<g.argc; i++){
char *zName;
|
| ︙ | ︙ |
Changes to src/attach.c.
| ︙ | ︙ | |||
73 74 75 76 77 78 79 |
for(i=0; zFilename[i]; i++){
if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
zFilename = &zFilename[i+1];
i = -1;
}
}
if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
| | | | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
for(i=0; zFilename[i]; i++){
if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
zFilename = &zFilename[i+1];
i = -1;
}
}
if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
}else{
zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
}
@
@ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
@ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br />
if( zComment ) while( isspace(zComment[0]) ) zComment++;
if( zComment && zComment[0] ){
@ %w(zComment)<br />
}
if( zPage==0 && zTkt==0 ){
if( zSrc==0 || zSrc[0]==0 ){
zSrc = "Deleted from";
}else {
zSrc = "Added to";
}
|
| ︙ | ︙ | |||
271 272 273 274 275 276 277 |
cgi_redirect(zFrom);
}
style_header("Add Attachment");
@ <h2>Add Attachment To %s(zTargetType)</h2>
@ <form action="%s(g.zBaseURL)/attachadd" method="POST"
@ enctype="multipart/form-data">
@ File to Attach:
| | | | | 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
cgi_redirect(zFrom);
}
style_header("Add Attachment");
@ <h2>Add Attachment To %s(zTargetType)</h2>
@ <form action="%s(g.zBaseURL)/attachadd" method="POST"
@ enctype="multipart/form-data">
@ File to Attach:
@ <input type="file" name="f" size="60"><br />
@ Description:<br />
@ <textarea name="comment" cols=80 rows=5 wrap="virtual"></textarea><br />
if( zTkt ){
@ <input type="hidden" name="tkt" value="%h(zTkt)">
}else{
@ <input type="hidden" name="page" value="%h(zPage)">
}
@ <input type="hidden" name="from" value="%h(zFrom)">
@ <input type="submit" name="ok" value="Add Attachment">
|
| ︙ | ︙ | |||
349 350 351 352 353 354 355 |
manifest_crosslink(rid, &manifest);
db_end_transaction(0);
cgi_redirect(zFrom);
}
style_header("Delete Attachment");
@ <form action="%s(g.zBaseURL)/attachdelete" method="POST">
@ <p>Confirm that you want to delete the attachment named
| | | 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
manifest_crosslink(rid, &manifest);
db_end_transaction(0);
cgi_redirect(zFrom);
}
style_header("Delete Attachment");
@ <form action="%s(g.zBaseURL)/attachdelete" method="POST">
@ <p>Confirm that you want to delete the attachment named
@ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br />
if( zTkt ){
@ <input type="hidden" name="tkt" value="%h(zTkt)">
}else{
@ <input type="hidden" name="page" value="%h(zPage)">
}
@ <input type="hidden" name="file" value="%h(zFile)">
@ <input type="hidden" name="from" value="%h(zFrom)">
@ <input type="submit" name="confirm" value="Delete">
@ <input type="submit" name="cancel" value="Cancel">
@ </form>
style_footer();
}
|
Changes to src/blob.c.
| ︙ | ︙ | |||
658 659 660 661 662 663 664 |
zName = zBuf;
strcpy(zName, zFilename);
}
nName = file_simplify_name(zName, nName);
for(i=1; i<nName; i++){
if( zName[i]=='/' ){
zName[i] = 0;
| | | | 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 |
zName = zBuf;
strcpy(zName, zFilename);
}
nName = file_simplify_name(zName, nName);
for(i=1; i<nName; i++){
if( zName[i]=='/' ){
zName[i] = 0;
#if defined(_WIN32)
/*
** On Windows, local path looks like: C:/develop/project/file.txt
** The if stops us from trying to create a directory of a drive letter
** C: in this example.
*/
if( !(i==2 && zName[1]==':') ){
#endif
if( file_mkdir(zName, 1) ){
fossil_fatal_recursive("unable to create directory %s", zName);
return 0;
}
#if defined(_WIN32)
}
#endif
zName[i] = '/';
}
}
out = fopen(zName, "wb");
if( out==0 ){
|
| ︙ | ︙ | |||
854 855 856 857 858 859 860 |
blob_reset(&b1);
blob_reset(&b2);
blob_reset(&b3);
}
printf("ok\n");
}
| | | 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 |
blob_reset(&b1);
blob_reset(&b2);
blob_reset(&b3);
}
printf("ok\n");
}
#if defined(_WIN32)
/*
** Convert every \n character in the given blob into \r\n.
*/
void blob_add_cr(Blob *p){
char *z = p->aData;
int j = p->nUsed;
int i, n;
|
| ︙ | ︙ | |||
894 895 896 897 898 899 900 |
z = p->aData;
for(i=j=0; z[i]; i++){
if( z[i]!='\r' ) z[j++] = z[i];
}
z[j] = 0;
p->nUsed = j;
}
| > > > > > > > > > > > > > > > > > > > > > | 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 |
z = p->aData;
for(i=j=0; z[i]; i++){
if( z[i]!='\r' ) z[j++] = z[i];
}
z[j] = 0;
p->nUsed = j;
}
/*
** Shell-escape the given string. Append the result to a blob.
*/
void shell_escape(Blob *pBlob, const char *zIn){
int n = blob_size(pBlob);
int k = strlen(zIn);
int i, c;
char *z;
for(i=0; (c = zIn[i])!=0; i++){
if( isspace(c) || c=='"' || (c=='\\' && zIn[i+1]!=0) ){
blob_appendf(pBlob, "\"%s\"", zIn);
z = blob_buffer(pBlob);
for(i=n+1; i<=n+k; i++){
if( z[i]=='"' ) z[i] = '_';
}
return;
}
}
blob_append(pBlob, zIn, -1);
}
|
Changes to src/branch.c.
| ︙ | ︙ | |||
213 214 215 216 217 218 219 220 221 222 223 |
** WEBPAGE: brlist
**
** Show a timeline of all branches
*/
void brlist_page(void){
Stmt q;
int cnt;
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
| > | > > > > > > | > | > | > > | | | | | | | < < < < < < < < < < | < < < < < < < < | | | | | | | | | | > > | > > > | < < | | 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 |
** WEBPAGE: brlist
**
** Show a timeline of all branches
*/
void brlist_page(void){
Stmt q;
int cnt;
int showClosed = P("closed")!=0;
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
style_header(showClosed ? "Closed Branches" : "Open Branches");
style_submenu_element("Timeline", "Timeline", "brtimeline");
if( showClosed ){
style_submenu_element("Open","Open","brlist");
}else{
style_submenu_element("Closed","Closed","brlist?closed");
}
login_anonymous_available();
compute_leaves(0, 1);
style_sidebox_begin("Nomenclature:", "33%");
@ <ol>
@ <li> An <div class="sideboxDescribed"><a href="brlist">
@ open branch</a></div> is a branch that has one or
@ more <a href="leaves">open leaves.</a>
@ The presence of open leaves presumably means
@ that the branch is still being extended with new check-ins.</li>
@ <li> A <div class="sideboxDescribed"><a href="brlist?closed">
@ closed branch</a></div> is a branch with only
@ <div class="sideboxDescribed"><a href="leaves?closed">
@ closed leaves</a></div>.
@ Closed branches are fixed and do not change (unless they are first
@ reopened)</li>
@ </ol>
style_sidebox_end();
cnt = 0;
if( !showClosed ){
db_prepare(&q,
"SELECT DISTINCT value FROM tagxref"
" WHERE tagid=%d AND value NOT NULL"
" AND rid IN leaves"
" ORDER BY value /*sort*/",
TAG_BRANCH
);
}else{
db_prepare(&q,
"SELECT value FROM tagxref"
" WHERE tagid=%d AND value NOT NULL"
" EXCEPT "
"SELECT value FROM tagxref"
" WHERE tagid=%d AND value NOT NULL"
" AND rid IN leaves"
" ORDER BY value /*sort*/",
TAG_BRANCH, TAG_BRANCH
);
}
while( db_step(&q)==SQLITE_ROW ){
const char *zBr = db_column_text(&q, 0);
if( cnt==0 ){
if( showClosed ){
@ <h2>Closed Branches:</h2>
}else{
@ <h2>Open Branches:</h2>
}
@ <ul>
cnt++;
}
if( g.okHistory ){
@ <li><a href="%s(g.zBaseURL)/timeline?r=%T(zBr)">%h(zBr)</a></li>
}else{
@ <li><b>%h(zBr)</b></li>
}
}
if( cnt ){
@ </ul>
}
db_finalize(&q);
@ <script type="text/JavaScript">
@ function xin(id){
@ }
@ function xout(id){
@ }
@ </script>
style_footer();
}
|
| ︙ | ︙ | |||
344 345 346 347 348 349 350 |
"%s AND blob.rid IN (SELECT rid FROM tagxref"
" WHERE tagtype>0 AND tagid=%d AND srcid!=0)"
" ORDER BY event.mtime DESC",
timeline_query_for_www(), TAG_BRANCH
);
www_print_timeline(&q, 0, brtimeline_extra);
db_finalize(&q);
| < | | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
"%s AND blob.rid IN (SELECT rid FROM tagxref"
" WHERE tagtype>0 AND tagid=%d AND srcid!=0)"
" ORDER BY event.mtime DESC",
timeline_query_for_www(), TAG_BRANCH
);
www_print_timeline(&q, 0, brtimeline_extra);
db_finalize(&q);
@ <script type="text/JavaScript">
@ function xin(id){
@ }
@ function xout(id){
@ }
@ </script>
style_footer();
}
|
Changes to src/browse.c.
| ︙ | ︙ | |||
142 143 144 145 146 147 148 |
if( zCI ){
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
char zShort[20];
memcpy(zShort, zUuid, 10);
zShort[10] = 0;
@ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]
@ %s(blob_str(&dirname))</h2>
| | | | | 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 |
if( zCI ){
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
char zShort[20];
memcpy(zShort, zUuid, 10);
zShort[10] = 0;
@ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]
@ %s(blob_str(&dirname))</h2>
zSubdirLink = mprintf("%s/dir?ci=%S&name=%T", g.zTop, zUuid, zPrefix);
if( zD ){
style_submenu_element("Top", "Top", "%s/dir?ci=%S", g.zTop, zUuid);
style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
}else{
style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
}
}else{
@ <h2>The union of all files from all check-ins
@ %s(blob_str(&dirname))</h2>
zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
if( zD ){
style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
style_submenu_element("Tip", "Tip", "%s/dir?name=%t&ci=tip",
g.zBaseURL, zD);
style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk",
g.zBaseURL,zD);
}else{
style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
}
}
|
| ︙ | ︙ | |||
214 215 216 217 218 219 220 | ** directory. */ mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/"); nCol = 4; nRow = (cnt+nCol-1)/nCol; db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/"); | | | | | > | | 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 |
** directory.
*/
mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/");
nCol = 4;
nRow = (cnt+nCol-1)/nCol;
db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/");
@ <table class="browser"><tr><td class="browser"><ul class="browser">
i = 0;
while( db_step(&q)==SQLITE_ROW ){
const char *zFN;
if( i==nRow ){
@ </ul></td><td class="browser"><ul class="browser">
i = 0;
}
i++;
zFN = db_column_text(&q, 0);
if( zFN[0]=='/' ){
zFN++;
@ <li><a href="%s(zSubdirLink)%T(zFN)">%h(zFN)/</a></li>
}else if( zCI ){
const char *zUuid = db_column_text(&q, 1);
@ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFN)</a></li>
}else{
@ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)
@ </a></li>
}
}
db_finalize(&q);
@ </ul></td></tr></table>
style_footer();
}
|
Changes to src/captcha.c.
| ︙ | ︙ | |||
397 398 399 400 401 402 403 |
printf("%s:\n%s", zHex, z);
free(z);
}
}
/*
** Compute a seed value for a captcha. The seed is public and is sent
| | | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 |
printf("%s:\n%s", zHex, z);
free(z);
}
}
/*
** Compute a seed value for a captcha. The seed is public and is sent
** as a hidden parameter with the page that contains the captcha. Knowledge
** of the seed is insufficient for determining the captcha without additional
** information held only on the server and never revealed.
*/
unsigned int captcha_seed(void){
unsigned int x;
sqlite3_randomness(sizeof(x), &x);
x &= 0x7fffffff;
|
| ︙ | ︙ |
Changes to src/cgi.c.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 | ** This file contains C functions and procedures that provide useful ** services to CGI programs. There are procedures for parsing and ** dispensing QUERY_STRING parameters and cookies, the "mprintf()" ** formatting function and its cousins, and routines to encode and ** decode strings in HTML or HTTP. */ #include "config.h" | | | | | > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | ** This file contains C functions and procedures that provide useful ** services to CGI programs. There are procedures for parsing and ** dispensing QUERY_STRING parameters and cookies, the "mprintf()" ** formatting function and its cousins, and routines to encode and ** decode strings in HTML or HTTP. */ #include "config.h" #ifdef _WIN32 # include <windows.h> /* for Sleep once server works again */ # if defined(__MINGW32__) # define sleep Sleep /* windows does not have sleep, but Sleep */ # include <ws2tcpip.h> # endif #else # include <sys/socket.h> # include <netinet/in.h> # include <arpa/inet.h> # include <sys/times.h> # include <sys/time.h> # include <sys/wait.h> |
| ︙ | ︙ | |||
849 850 851 852 853 854 855 |
cgi_parameter("",""); /* Force the parameters into sorted order */
for(i=0; i<nUsedQP; i++){
cgi_printf("%s = %s <br />\n",
htmlize(aParamQP[i].zName, -1), htmlize(aParamQP[i].zValue, -1));
}
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 850 851 852 853 854 855 856 857 858 859 860 861 862 863 |
cgi_parameter("",""); /* Force the parameters into sorted order */
for(i=0; i<nUsedQP; i++){
cgi_printf("%s = %s <br />\n",
htmlize(aParamQP[i].zName, -1), htmlize(aParamQP[i].zValue, -1));
}
}
/*
** This routine works like "printf" except that it has the
** extra formatting capabilities such as %h and %t.
*/
void cgi_printf(const char *zFormat, ...){
va_list ap;
va_start(ap,zFormat);
|
| ︙ | ︙ | |||
1120 1121 1122 1123 1124 1125 1126 |
zFieldName = extract_token(zLine,&zVal);
if( zFieldName==0 || *zFieldName==0 ) break;
while( isspace(*zVal) ){ zVal++; }
i = strlen(zVal);
while( i>0 && isspace(zVal[i-1]) ){ i--; }
zVal[i] = 0;
for(i=0; zFieldName[i]; i++){ zFieldName[i] = tolower(zFieldName[i]); }
| < < | < < < < > > > > > > > > > > > > > > > > > > > | | > > > | > | 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 |
zFieldName = extract_token(zLine,&zVal);
if( zFieldName==0 || *zFieldName==0 ) break;
while( isspace(*zVal) ){ zVal++; }
i = strlen(zVal);
while( i>0 && isspace(zVal[i-1]) ){ i--; }
zVal[i] = 0;
for(i=0; zFieldName[i]; i++){ zFieldName[i] = tolower(zFieldName[i]); }
if( strcmp(zFieldName,"content-length:")==0 ){
cgi_setenv("CONTENT_LENGTH", zVal);
}else if( strcmp(zFieldName,"content-type:")==0 ){
cgi_setenv("CONTENT_TYPE", zVal);
}else if( strcmp(zFieldName,"cookie:")==0 ){
cgi_setenv("HTTP_COOKIE", zVal);
}else if( strcmp(zFieldName,"https:")==0 ){
cgi_setenv("HTTPS", zVal);
}else if( strcmp(zFieldName,"host:")==0 ){
cgi_setenv("HTTP_HOST", zVal);
}else if( strcmp(zFieldName,"if-none-match:")==0 ){
cgi_setenv("HTTP_IF_NONE_MATCH", zVal);
}else if( strcmp(zFieldName,"if-modified-since:")==0 ){
cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal);
}
#if 0
else if( strcmp(zFieldName,"referer:")==0 ){
cgi_setenv("HTTP_REFERER", zVal);
}else if( strcmp(zFieldName,"user-agent:")==0 ){
cgi_setenv("HTTP_USER_AGENT", zVal);
}
#endif
}
cgi_init();
}
#if INTERFACE
/*
** Bitmap values for the flags parameter to cgi_http_server().
*/
#define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */
#endif /* INTERFACE */
/*
** Maximum number of child processes that we can have running
** at one time before we start slowing things down.
*/
#define MAX_PARALLEL 2
/*
** Implement an HTTP server daemon listening on port iPort.
**
** As new connections arrive, fork a child and let child return
** out of this procedure call. The child will handle the request.
** The parent never returns from this procedure.
**
** Return 0 to each child as it runs. If unable to establish a
** listening socket, return non-zero.
*/
int cgi_http_server(int mnPort, int mxPort, char *zBrowser, int flags){
#if defined(_WIN32)
/* Use win32_http_server() instead */
fossil_exit(1);
#else
int listener = -1; /* The server socket */
int connection; /* A socket for each individual connection */
fd_set readfds; /* Set of file descriptors for select() */
size_t lenaddr; /* Length of the inaddr structure */
int child; /* PID of the child process */
int nchildren = 0; /* Number of child processes */
struct timeval delay; /* How long to wait inside select() */
struct sockaddr_in inaddr; /* The socket address */
int opt = 1; /* setsockopt flag */
int iPort = mnPort;
while( iPort<=mxPort ){
memset(&inaddr, 0, sizeof(inaddr));
inaddr.sin_family = AF_INET;
if( flags & HTTP_SERVER_LOCALHOST ){
inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
}else{
inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
}
inaddr.sin_port = htons(iPort);
listener = socket(AF_INET, SOCK_STREAM, 0);
if( listener<0 ){
iPort++;
continue;
}
|
| ︙ | ︙ | |||
1222 1223 1224 1225 1226 1227 1228 |
/* Slow down if connections are arriving too fast */
sleep( nchildren-MAX_PARALLEL );
}
delay.tv_sec = 60;
delay.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET( listener, &readfds);
| | > | 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 |
/* Slow down if connections are arriving too fast */
sleep( nchildren-MAX_PARALLEL );
}
delay.tv_sec = 60;
delay.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET( listener, &readfds);
select( listener+1, &readfds, 0, 0, &delay);
if( FD_ISSET(listener, &readfds) ){
lenaddr = sizeof(inaddr);
connection = accept(listener, (struct sockaddr*)&inaddr,
(socklen_t*) &lenaddr);
if( connection>=0 ){
child = fork();
if( child!=0 ){
if( child>0 ) nchildren++;
|
| ︙ | ︙ |
Changes to src/checkin.c.
| ︙ | ︙ | |||
399 400 401 402 403 404 405 |
if( zEditor==0 ){
zEditor = getenv("VISUAL");
}
if( zEditor==0 ){
zEditor = getenv("EDITOR");
}
if( zEditor==0 ){
| | | | 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 |
if( zEditor==0 ){
zEditor = getenv("VISUAL");
}
if( zEditor==0 ){
zEditor = getenv("EDITOR");
}
if( zEditor==0 ){
#if defined(_WIN32)
zEditor = "notepad";
#else
zEditor = "ed";
#endif
}
zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
g.zLocalRoot);
#if defined(_WIN32)
blob_add_cr(&text);
#endif
blob_write_to_file(&text, zFile);
zCmd = mprintf("%s \"%s\"", zEditor, zFile);
printf("%s\n", zCmd);
if( portable_system(zCmd) ){
fossil_panic("editor aborted");
|
| ︙ | ︙ | |||
511 512 513 514 515 516 517 |
b = db_exists(
"SELECT 1 FROM event"
" WHERE datetime(mtime)>=%Q"
" AND type='ci' AND objid=%d",
zDate, rid
);
if( b ){
| | | > > > > > > > > > > > > > > > > > | 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 |
b = db_exists(
"SELECT 1 FROM event"
" WHERE datetime(mtime)>=%Q"
" AND type='ci' AND objid=%d",
zDate, rid
);
if( b ){
fossil_fatal("ancestor check-in [%.10s] (%s) is younger (clock skew?)"
" Use -f to override.", zUuid, zDate);
}
#endif
}
/*
** zDate should be a valid date string. Convert this string into the
** format YYYY-MM-DDTHH:MM:SS. If the string is not a valid date,
** print a fatal error and quit.
*/
char *date_in_standard_format(const char *zInputDate){
char *zDate = db_text(0, "SELECT datetime(%Q)", zInputDate);
if( zDate[0]==0 ){
fossil_fatal("unrecognized date format (%s): use \"YYYY-MM-DD HH:MM:SS\"",
zInputDate);
}
assert( strlen(zDate)==19 );
assert( zDate[10]==' ' );
zDate[10] = 'T';
return zDate;
}
/*
** COMMAND: ci
** COMMAND: commit
**
** Usage: %fossil commit ?OPTIONS? ?FILE...?
**
|
| ︙ | ︙ | |||
737 738 739 740 741 742 743 |
/* Create the manifest */
blob_zero(&manifest);
if( blob_size(&comment)==0 ){
blob_append(&comment, "(no comment)", -1);
}
blob_appendf(&manifest, "C %F\n", blob_str(&comment));
| | < | | 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 |
/* Create the manifest */
blob_zero(&manifest);
if( blob_size(&comment)==0 ){
blob_append(&comment, "(no comment)", -1);
}
blob_appendf(&manifest, "C %F\n", blob_str(&comment));
zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
blob_appendf(&manifest, "D %s\n", zDate);
zDate[10] = ' ';
db_prepare(&q,
"SELECT pathname, uuid, origname, blob.rid, isexe"
" FROM vfile JOIN blob ON vfile.mrid=blob.rid"
" WHERE NOT deleted AND vfile.vid=%d"
" ORDER BY 1", vid);
blob_zero(&filename);
blob_appendf(&filename, "%s", g.zLocalRoot);
nBasename = blob_size(&filename);
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
const char *zUuid = db_column_text(&q, 1);
const char *zOrig = db_column_text(&q, 2);
int frid = db_column_int(&q, 3);
int isexe = db_column_int(&q, 4);
const char *zPerm;
blob_append(&filename, zName, -1);
#if !defined(_WIN32)
/* For unix, extract the "executable" permission bit directly from
** the filesystem. On windows, the "executable" bit is retained
** unchanged from the original. */
isexe = file_isexe(blob_str(&filename));
#endif
if( isexe ){
zPerm = " x";
|
| ︙ | ︙ | |||
781 782 783 784 785 786 787 |
}
if( !g.markPrivate ) content_make_public(frid);
}
blob_reset(&filename);
db_finalize(&q);
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
blob_appendf(&manifest, "P %s", zUuid);
| < > > | | | | | | | | | | | | | > | 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 |
}
if( !g.markPrivate ) content_make_public(frid);
}
blob_reset(&filename);
db_finalize(&q);
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
blob_appendf(&manifest, "P %s", zUuid);
if( !forceFlag ){
checkin_verify_younger(vid, zUuid, zDate);
db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
db_bind_int(&q2, ":id", 0);
while( db_step(&q2)==SQLITE_ROW ){
int mid = db_column_int(&q2, 0);
if( !g.markPrivate && content_is_private(mid) ) continue;
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
if( zUuid ){
blob_appendf(&manifest, " %s", zUuid);
checkin_verify_younger(mid, zUuid, zDate);
free(zUuid);
}
}
db_finalize(&q2);
}
blob_appendf(&manifest, "\n");
blob_appendf(&manifest, "R %b\n", &cksum1);
if( zBranch && zBranch[0] ){
Stmt q;
if( zBgColor && zBgColor[0] ){
blob_appendf(&manifest, "T *bgcolor * %F\n", zBgColor);
|
| ︙ | ︙ |
Changes to src/config.h.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 | #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include <stdarg.h> #include <assert.h> | > > > > | > | > > | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER)
# if defined(__DMC__) || defined(_MSC_VER)
typedef int socklen_t;
# endif
# ifndef _WIN32
# define _WIN32
# endif
#else
# include <sys/types.h>
# include <signal.h>
# include <pwd.h>
#endif
#include "sqlite3.h"
/*
** Typedef for a 64-bit integer
|
| ︙ | ︙ |
Changes to src/configure.c.
| ︙ | ︙ | |||
457 458 459 460 461 462 463 464 465 466 467 468 469 470 |
fossil_fatal("no server specified");
}
zPw = db_get("last-sync-pw", 0);
}
url_parse(zServer);
if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw);
user_select();
if( strncmp(zMethod, "push", n)==0 ){
client_sync(0,0,0,0,mask);
}else{
client_sync(0,0,0,mask,0);
}
}else
if( strncmp(zMethod, "reset", n)==0 ){
| > | 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 |
fossil_fatal("no server specified");
}
zPw = db_get("last-sync-pw", 0);
}
url_parse(zServer);
if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw);
user_select();
url_enable_proxy("via proxy: ");
if( strncmp(zMethod, "push", n)==0 ){
client_sync(0,0,0,0,mask);
}else{
client_sync(0,0,0,mask,0);
}
}else
if( strncmp(zMethod, "reset", n)==0 ){
|
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 32 33 34 35 36 37 38 | ** (2) The "repository" database ** ** (3) A local checkout database named "_FOSSIL_" or ".fos" ** and located at the root of the local copy of the source tree. ** */ #include "config.h" #include <sqlite3.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include "db.h" #if INTERFACE | > > > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | ** (2) The "repository" database ** ** (3) A local checkout database named "_FOSSIL_" or ".fos" ** and located at the root of the local copy of the source tree. ** */ #include "config.h" #if ! defined(_WIN32) # include <pwd.h> #endif #include <sqlite3.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include "db.h" #if INTERFACE |
| ︙ | ︙ | |||
132 133 134 135 136 137 138 139 140 141 142 143 |
void db_force_rollback(void){
static int busy = 0;
if( busy || g.db==0 ) return;
busy = 1;
undo_rollback();
if( nBegin ){
sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0);
if( isNewRepo ){
db_close();
unlink(g.zRepositoryName);
}
}
| > < | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
void db_force_rollback(void){
static int busy = 0;
if( busy || g.db==0 ) return;
busy = 1;
undo_rollback();
if( nBegin ){
sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0);
nBegin = 0;
if( isNewRepo ){
db_close();
unlink(g.zRepositoryName);
}
}
busy = 0;
}
/*
** Install a commit hook. Hooks are installed in sequence order.
** It is an error to install the same commit hook more than once.
**
|
| ︙ | ︙ | |||
532 533 534 535 536 537 538 |
}else{
z = 0;
}
db_finalize(&s);
return z;
}
| | | | 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 |
}else{
z = 0;
}
db_finalize(&s);
return z;
}
#if defined(_WIN32)
extern char *sqlite3_win32_mbcs_to_utf8(const char*);
#endif
/*
** Initialize a new database file with the given schema. If anything
** goes wrong, call db_err() to exit.
*/
void db_init_database(
const char *zFileName, /* Name of database file to create */
const char *zSchema, /* First part of schema */
... /* Additional SQL to run. Terminate with NULL. */
){
sqlite3 *db;
int rc;
const char *zSql;
va_list ap;
#if defined(_WIN32)
zFileName = sqlite3_win32_mbcs_to_utf8(zFileName);
#endif
rc = sqlite3_open(zFileName, &db);
if( rc!=SQLITE_OK ){
db_err(sqlite3_errmsg(db));
}
sqlite3_busy_timeout(db, 5000);
|
| ︙ | ︙ | |||
585 586 587 588 589 590 591 |
*/
static sqlite3 *openDatabase(const char *zDbName){
int rc;
const char *zVfs;
sqlite3 *db;
zVfs = getenv("FOSSIL_VFS");
| | | 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 |
*/
static sqlite3 *openDatabase(const char *zDbName){
int rc;
const char *zVfs;
sqlite3 *db;
zVfs = getenv("FOSSIL_VFS");
#if defined(_WIN32)
zDbName = sqlite3_win32_mbcs_to_utf8(zDbName);
#endif
rc = sqlite3_open_v2(
zDbName, &db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
zVfs
);
|
| ︙ | ︙ | |||
613 614 615 616 617 618 619 |
*/
void db_open_or_attach(const char *zDbName, const char *zLabel){
if( !g.db ){
g.db = openDatabase(zDbName);
g.zRepoDb = "main";
db_connection_init();
}else{
| | | 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 |
*/
void db_open_or_attach(const char *zDbName, const char *zLabel){
if( !g.db ){
g.db = openDatabase(zDbName);
g.zRepoDb = "main";
db_connection_init();
}else{
#if defined(_WIN32)
zDbName = sqlite3_win32_mbcs_to_utf8(zDbName);
#endif
db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel);
g.zRepoDb = mprintf("%s", zLabel);
}
}
|
| ︙ | ︙ | |||
637 638 639 640 641 642 643 |
** connection so that we can join between the various databases. In that
** case, invoke this routine with useAttach as 1.
*/
void db_open_config(int useAttach){
char *zDbName;
const char *zHome;
if( g.configOpen ) return;
| | | 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 |
** connection so that we can join between the various databases. In that
** case, invoke this routine with useAttach as 1.
*/
void db_open_config(int useAttach){
char *zDbName;
const char *zHome;
if( g.configOpen ) return;
#if defined(_WIN32)
zHome = getenv("LOCALAPPDATA");
if( zHome==0 ){
zHome = getenv("APPDATA");
if( zHome==0 ){
zHome = getenv("HOMEPATH");
}
}
|
| ︙ | ︙ | |||
659 660 661 662 663 664 665 |
fossil_fatal("cannot locate home directory - "
"please set the HOME environment variable");
}
#endif
if( file_isdir(zHome)!=1 ){
fossil_fatal("invalid home directory: %s", zHome);
}
| | | | 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 |
fossil_fatal("cannot locate home directory - "
"please set the HOME environment variable");
}
#endif
if( file_isdir(zHome)!=1 ){
fossil_fatal("invalid home directory: %s", zHome);
}
#ifndef _WIN32
if( access(zHome, W_OK) ){
fossil_fatal("home directory %s must be writeable", zHome);
}
#endif
g.zHome = mprintf("%/", zHome);
#if defined(_WIN32)
/* . filenames give some window systems problems and many apps problems */
zDbName = mprintf("%//_fossil", zHome);
#else
zDbName = mprintf("%s/.fossil", zHome);
#endif
if( file_size(zDbName)<1024*3 ){
db_init_database(zDbName, zConfigSchema, (char*)0);
|
| ︙ | ︙ | |||
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 |
db_open_repository(0);
}
/*
** Close the database connection.
*/
void db_close(void){
if( g.db==0 ) return;
while( pAllStmt ){
db_finalize(pAllStmt);
}
db_end_transaction(1);
g.repositoryOpen = 0;
g.localOpen = 0;
g.configOpen = 0;
sqlite3_close(g.db);
g.db = 0;
}
/*
** Create a new empty repository database with the given name.
**
** Only the schema is initialized. The required VAR tables entries
| > > > > > > > > > > | 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 |
db_open_repository(0);
}
/*
** Close the database connection.
*/
void db_close(void){
sqlite3_stmt *pStmt;
if( g.db==0 ) return;
while( pAllStmt ){
db_finalize(pAllStmt);
}
db_end_transaction(1);
pStmt = 0;
while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){
fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt));
}
g.repositoryOpen = 0;
g.localOpen = 0;
g.configOpen = 0;
sqlite3_wal_checkpoint(g.db, 0);
sqlite3_close(g.db);
g.db = 0;
if( g.dbConfig ){
sqlite3_close(g.dbConfig);
g.dbConfig = 0;
}
}
/*
** Create a new empty repository database with the given name.
**
** Only the schema is initialized. The required VAR tables entries
|
| ︙ | ︙ | |||
932 933 934 935 936 937 938 |
void db_create_default_users(int setupUserOnly, const char *zDefaultUser){
const char *zUser;
zUser = db_get("default-user", 0);
if( zUser==0 ){
zUser = zDefaultUser;
}
if( zUser==0 ){
| | | 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 |
void db_create_default_users(int setupUserOnly, const char *zDefaultUser){
const char *zUser;
zUser = db_get("default-user", 0);
if( zUser==0 ){
zUser = zDefaultUser;
}
if( zUser==0 ){
#if defined(_WIN32)
zUser = getenv("USERNAME");
#else
zUser = getenv("USER");
#endif
}
if( zUser==0 ){
zUser = "root";
|
| ︙ | ︙ | |||
998 999 1000 1001 1002 1003 1004 |
db_create_default_users(0, zDefaultUser);
user_select();
if( zInitialDate ){
int rid;
blob_zero(&manifest);
blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n");
| | < | 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 |
db_create_default_users(0, zDefaultUser);
user_select();
if( zInitialDate ){
int rid;
blob_zero(&manifest);
blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n");
zDate = date_in_standard_format(zInitialDate);
blob_appendf(&manifest, "D %s\n", zDate);
blob_appendf(&manifest, "P\n");
md5sum_init();
blob_appendf(&manifest, "R %s\n", md5sum_finish(0));
blob_appendf(&manifest, "T *branch * trunk\n");
blob_appendf(&manifest, "T *sym-trunk *\n");
blob_appendf(&manifest, "U %F\n", g.zLogin);
|
| ︙ | ︙ | |||
1251 1252 1253 1254 1255 1256 1257 |
** If g.useAttach that means the ~/.fossil database was opened with
** the useAttach flag set to 1. In that case no connection swap is required
** so this routine is a no-op.
*/
void db_swap_connections(void){
if( !g.useAttach ){
sqlite3 *dbTemp = g.db;
| < | 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 |
** If g.useAttach that means the ~/.fossil database was opened with
** the useAttach flag set to 1. In that case no connection swap is required
** so this routine is a no-op.
*/
void db_swap_connections(void){
if( !g.useAttach ){
sqlite3 *dbTemp = g.db;
g.db = g.dbConfig;
g.dbConfig = dbTemp;
}
}
/*
** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the
|
| ︙ | ︙ | |||
1372 1373 1374 1375 1376 1377 1378 |
}
void db_lset_int(const char *zName, int value){
db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
}
/*
** Record the name of a local repository in the global_config() database.
| | | 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 |
}
void db_lset_int(const char *zName, int value){
db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
}
/*
** Record the name of a local repository in the global_config() database.
** The repository filename %s is recorded as an entry with a "name" field
** of the following form:
**
** repo:%s
**
** The value field is set to 1.
*/
void db_record_repository_filename(const char *zName){
|
| ︙ | ︙ | |||
1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 |
** pgp-command Command used to clear-sign manifests at check-in.
** The default is "gpg --clearsign -o ".
**
** proxy URL of the HTTP proxy. If undefined or "off" then
** the "http_proxy" environment variable is consulted.
** If the http_proxy environment variable is undefined
** then a direct HTTP connection is used.
**
** web-browser A shell command used to launch your preferred
** web browser when given a URL as an argument.
** Defaults to "start" on windows, "open" on Mac,
** and "firefox" on Unix.
*/
void setting_cmd(void){
| > > > | 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 |
** pgp-command Command used to clear-sign manifests at check-in.
** The default is "gpg --clearsign -o ".
**
** proxy URL of the HTTP proxy. If undefined or "off" then
** the "http_proxy" environment variable is consulted.
** If the http_proxy environment variable is undefined
** then a direct HTTP connection is used.
**
** ssh-command Command used to talk to a remote machine with
** the "ssh://" protocol.
**
** web-browser A shell command used to launch your preferred
** web browser when given a URL as an argument.
** Defaults to "start" on windows, "open" on Mac,
** and "firefox" on Unix.
*/
void setting_cmd(void){
|
| ︙ | ︙ | |||
1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 |
"gdiff-command",
"ignore-glob",
"http-port",
"localauth",
"mtime-changes",
"pgp-command",
"proxy",
"web-browser",
};
int i;
int globalFlag = find_option("global","g",0)!=0;
int unsetFlag = g.argv[1][0]=='u';
db_open_config(1);
db_find_and_open_repository(0);
| > | 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 |
"gdiff-command",
"ignore-glob",
"http-port",
"localauth",
"mtime-changes",
"pgp-command",
"proxy",
"ssh-command",
"web-browser",
};
int i;
int globalFlag = find_option("global","g",0)!=0;
int unsetFlag = g.argv[1][0]=='u';
db_open_config(1);
db_find_and_open_repository(0);
|
| ︙ | ︙ |
Changes to src/descendants.c.
| ︙ | ︙ | |||
316 317 318 319 320 321 322 |
style_submenu_element("Open", "Open", "leaves");
}
style_header("Leaves");
login_anonymous_available();
compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1);
style_sidebox_begin("Nomenclature:", "33%");
@ <ol>
| > | > | > | | | | 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 |
style_submenu_element("Open", "Open", "leaves");
}
style_header("Leaves");
login_anonymous_available();
compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1);
style_sidebox_begin("Nomenclature:", "33%");
@ <ol>
@ <li> A <div class="sideboxDescribed">leaf</div>
@ is a check-in with no descendants.</li>
@ <li> An <div class="sideboxDescribed">open leaf</div>
@ is a leaf that does not have a "closed" tag
@ and is thus assumed to still be in use.</li>
@ <li> A <div class="sideboxDescribed">closed leaf</div>
@ has a "closed" tag and is thus assumed to
@ be historical and no longer in active use.</li>
@ </ol>
style_sidebox_end();
if( showAll ){
@ <h1>All leaves, both open and closed:</h1>
}else if( showClosed ){
@ <h1>Closed leaves:</h1>
}else{
@ <h1>Open leaves:</h1>
}
db_prepare(&q,
"%s"
" AND blob.rid IN leaves"
" ORDER BY event.mtime DESC",
timeline_query_for_www()
);
www_print_timeline(&q, TIMELINE_LEAFONLY, leaves_extra);
db_finalize(&q);
@ <br />
@ <script type="text/JavaScript">
@ function xin(id){
@ }
@ function xout(id){
@ }
@ </script>
style_footer();
}
|
Changes to src/diff.c.
| ︙ | ︙ | |||
63 64 65 66 67 68 69 | }; /* ** Return an array of DLine objects containing a pointer to the ** start of each line and a hash of that line. The lower ** bits of the hash store the length of each line. ** | | > > | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
};
/*
** Return an array of DLine objects containing a pointer to the
** start of each line and a hash of that line. The lower
** bits of the hash store the length of each line.
**
** Trailing whitespace is removed from each line. 2010-08-20: Not any
** more. If trailing whitespace is ignored, the "patch" command gets
** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b]
**
** Return 0 if the file is binary or contains a line that is
** too long.
*/
static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
int nLine, i, j, k, x;
unsigned int h, h2;
DLine *a;
/* Count the number of lines. Allocate space to hold
** the returned array.
*/
|
| ︙ | ︙ | |||
100 101 102 103 104 105 106 |
if( a==0 ) fossil_panic("out of memory");
memset(a, 0, nLine*sizeof(a[0]) );
/* Fill in the array */
for(i=0; i<nLine; i++){
a[i].z = z;
for(j=0; z[j] && z[j]!='\n'; j++){}
| > | | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
if( a==0 ) fossil_panic("out of memory");
memset(a, 0, nLine*sizeof(a[0]) );
/* Fill in the array */
for(i=0; i<nLine; i++){
a[i].z = z;
for(j=0; z[j] && z[j]!='\n'; j++){}
k = j;
while( ignoreWS && k>0 && isspace(z[k-1]) ){ k--; }
for(h=0, x=0; x<k; x++){
h = h ^ (h<<2) ^ z[x];
}
a[i].h = h = (h<<LENGTH_MASK_SZ) | k;;
h2 = h % nLine;
a[i].iNext = a[h2].iHash;
a[h2].iHash = i+1;
|
| ︙ | ︙ | |||
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 |
m = R[r+nr*3];
if( m>nContext ) m = nContext;
for(j=0; j<m; j++){
appendDiffLine(pOut, " ", &B[b+j]);
}
}
}
/*
** Compare two blocks of text on lines iS1 through iE1-1 of the aFrom[]
** file and lines iS2 through iE2-1 of the aTo[] file. Locate a sequence
** of lines in these two blocks that are exactly the same. Return
** the bounds of the matching sequence.
*/
static void longestCommonSequence(
DContext *p, /* Two files being compared */
int iS1, int iE1, /* Range of lines in p->aFrom[] */
int iS2, int iE2, /* Range of lines in p->aTo[] */
int *piSX, int *piEX, /* Write p->aFrom[] common segment here */
int *piSY, int *piEY /* Write p->aTo[] common segment here */
){
double bestScore = -1e30; /* Best score seen so far */
int i, j; /* Loop counters */
int iSX, iSY, iEX, iEY; /* Current match */
double score; /* Current score */
int skew; /* How lopsided is the match */
int dist; /* Distance of match from center */
int mid; /* Center of the span */
int iSXb, iSYb, iEXb, iEYb; /* Best match so far */
int iSXp, iSYp, iEXp, iEYp; /* Previous match */
iSXb = iSXp = iS1;
iEXb = iEXp = iS1;
iSYb = iSYp = iS2;
iEYb = iEYp = iS2;
mid = (iE1 + iS1)/2;
for(i=iS1; i<iE1; i++){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
m = R[r+nr*3];
if( m>nContext ) m = nContext;
for(j=0; j<m; j++){
appendDiffLine(pOut, " ", &B[b+j]);
}
}
}
/*
** Compute the optimal longest common subsequence (LCS) using an
** exhaustive search. This version of the LCS is only used for
** shorter input strings since runtime is O(N*N) where N is the
** input string length.
*/
static void optimalLCS(
DContext *p, /* Two files being compared */
int iS1, int iE1, /* Range of lines in p->aFrom[] */
int iS2, int iE2, /* Range of lines in p->aTo[] */
int *piSX, int *piEX, /* Write p->aFrom[] common segment here */
int *piSY, int *piEY /* Write p->aTo[] common segment here */
){
int mxLength = 0; /* Length of longest common subsequence */
int i, j; /* Loop counters */
int k; /* Length of a candidate subsequence */
int iSXb = iS1; /* Best match so far */
int iSYb = iS2; /* Best match so far */
for(i=iS1; i<iE1-mxLength; i++){
for(j=iS2; j<iE2-mxLength; j++){
if( !same_dline(&p->aFrom[i], &p->aTo[j]) ) continue;
if( mxLength && !same_dline(&p->aFrom[i+mxLength], &p->aTo[j+mxLength]) ){
continue;
}
k = 1;
while( i+k<iE1 && j+k<iE2 && same_dline(&p->aFrom[i+k],&p->aTo[j+k]) ){
k++;
}
if( k>mxLength ){
iSXb = i;
iSYb = j;
mxLength = k;
}
}
}
*piSX = iSXb;
*piEX = iSXb + mxLength;
*piSY = iSYb;
*piEY = iSYb + mxLength;
}
/*
** Compare two blocks of text on lines iS1 through iE1-1 of the aFrom[]
** file and lines iS2 through iE2-1 of the aTo[] file. Locate a sequence
** of lines in these two blocks that are exactly the same. Return
** the bounds of the matching sequence.
**
** If there are two or more possible answers of the same length, the
** returned sequence should be the one closest to the center of the
** input range.
**
** Ideally, the common sequence should be the longest possible common
** sequence. However, an exact computation of LCS is O(N*N) which is
** way too slow for larger files. So this routine uses an O(N)
** heuristic approximation based on hashing that usually works about
** as well. But if the O(N) algorithm doesn't get a good solution
** and N is not too large, we fall back to an exact solution by
** calling optimalLCS().
*/
static void longestCommonSequence(
DContext *p, /* Two files being compared */
int iS1, int iE1, /* Range of lines in p->aFrom[] */
int iS2, int iE2, /* Range of lines in p->aTo[] */
int *piSX, int *piEX, /* Write p->aFrom[] common segment here */
int *piSY, int *piEY /* Write p->aTo[] common segment here */
){
double bestScore = -1e30; /* Best score seen so far */
int i, j; /* Loop counters */
int iSX, iSY, iEX, iEY; /* Current match */
double score; /* Current score */
int skew; /* How lopsided is the match */
int dist; /* Distance of match from center */
int mid; /* Center of the span */
int iSXb, iSYb, iEXb, iEYb; /* Best match so far */
int iSXp, iSYp, iEXp, iEYp; /* Previous match */
iSXb = iSXp = iS1;
iEXb = iEXp = iS1;
iSYb = iSYp = iS2;
iEYb = iEYp = iS2;
mid = (iE1 + iS1)/2;
for(i=iS1; i<iE1; i++){
|
| ︙ | ︙ | |||
352 353 354 355 356 357 358 |
}else{
iSXp = iSX;
iSYp = iSY;
iEXp = iEX;
iEYp = iEY;
}
}
| > > > > > | | | | > | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
}else{
iSXp = iSX;
iSYp = iSY;
iEXp = iEX;
iEYp = iEY;
}
}
if( iSXb==iEXb && (iE1-iS1)*(iE2-iS2)<400 ){
/* If no common sequence is found using the hashing heuristic and
** the input is not too big, use the expensive exact solution */
optimalLCS(p, iS1, iE1, iS2, iE2, piSX, piEX, piSY, piEY);
}else{
*piSX = iSXb;
*piSY = iSYb;
*piEX = iEXb;
*piEY = iEYb;
}
/* printf("LCS(%d..%d/%d..%d) = %d..%d/%d..%d\n",
iS1, iE1, iS2, iE2, *piSX, *piEX, *piSY, *piEY); */
}
/*
** Do a single step in the difference. Compute a sequence of
** copy/delete/insert steps that will convert lines iS1 through iE1-1 of
|
| ︙ | ︙ | |||
470 471 472 473 474 475 476 | ** file is encountered, 0 is returned and pOut is written with ** text "cannot compute difference between binary files". */ int *text_diff( Blob *pA_Blob, /* FROM file */ Blob *pB_Blob, /* TO file */ Blob *pOut, /* Write unified diff here if not NULL */ | | > | > | > | 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 |
** file is encountered, 0 is returned and pOut is written with
** text "cannot compute difference between binary files".
*/
int *text_diff(
Blob *pA_Blob, /* FROM file */
Blob *pB_Blob, /* TO file */
Blob *pOut, /* Write unified diff here if not NULL */
int nContext, /* Amount of context to unified diff */
int ignoreEolWs /* Ignore whitespace at the end of lines */
){
DContext c;
/* Prepare the input files */
memset(&c, 0, sizeof(c));
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
&c.nFrom, ignoreEolWs);
c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
&c.nTo, ignoreEolWs);
if( c.aFrom==0 || c.aTo==0 ){
free(c.aFrom);
free(c.aTo);
if( pOut ){
blob_appendf(pOut, "cannot compute difference between binary files\n");
}
return 0;
|
| ︙ | ︙ | |||
520 521 522 523 524 525 526 |
int i;
int *R;
if( g.argc<4 ) usage("FILE1 FILE2 ...");
blob_read_from_file(&a, g.argv[2]);
for(i=3; i<g.argc; i++){
if( i>3 ) printf("-------------------------------\n");
blob_read_from_file(&b, g.argv[i]);
| | | | 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 |
int i;
int *R;
if( g.argc<4 ) usage("FILE1 FILE2 ...");
blob_read_from_file(&a, g.argv[2]);
for(i=3; i<g.argc; i++){
if( i>3 ) printf("-------------------------------\n");
blob_read_from_file(&b, g.argv[i]);
R = text_diff(&a, &b, 0, 0, 0);
for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
printf(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]);
}
/* free(R); */
blob_reset(&b);
}
}
/*
** COMMAND: test-udiff
*/
void test_udiff_cmd(void){
Blob a, b, out;
if( g.argc!=4 ) usage("FILE1 FILE2");
blob_read_from_file(&a, g.argv[2]);
blob_read_from_file(&b, g.argv[3]);
blob_zero(&out);
text_diff(&a, &b, &out, 3, 0);
blob_write_to_file(&out, "-");
}
/**************************************************************************
** The basic difference engine is above. What follows is the annotation
** engine. Both are in the same file since they share many components.
*/
|
| ︙ | ︙ | |||
572 573 574 575 576 577 578 |
** to be annotated. The annotator takes control of the input Blob and
** will release it when it is finished with it.
*/
static int annotation_start(Annotator *p, Blob *pInput){
int i;
memset(p, 0, sizeof(*p));
| | | 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 |
** to be annotated. The annotator takes control of the input Blob and
** will release it when it is finished with it.
*/
static int annotation_start(Annotator *p, Blob *pInput){
int i;
memset(p, 0, sizeof(*p));
p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,1);
if( p->c.aTo==0 ){
return 1;
}
p->aOrig = malloc( sizeof(p->aOrig[0])*p->c.nTo );
if( p->aOrig==0 ) fossil_panic("out of memory");
for(i=0; i<p->c.nTo; i++){
p->aOrig[i].z = p->c.aTo[i].z;
|
| ︙ | ︙ | |||
600 601 602 603 604 605 606 |
*/
static int annotation_step(Annotator *p, Blob *pParent, char *zPName){
int i, j;
int lnTo;
/* Prepare the parent file to be diffed */
p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
| | | 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 |
*/
static int annotation_step(Annotator *p, Blob *pParent, char *zPName){
int i, j;
int lnTo;
/* Prepare the parent file to be diffed */
p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
&p->c.nFrom, 1);
if( p->c.aFrom==0 ){
return 1;
}
/* Compute the differences going from pParent to the file being
** annotated. */
diff_all(&p->c);
|
| ︙ | ︙ |
Changes to src/diffcmd.c.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 | ** ** This file contains code used to implement the "diff" command */ #include "config.h" #include "diffcmd.h" #include <assert.h> | < < < < < < < < < < < < < < < < < < < < < | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
**
** This file contains code used to implement the "diff" command
*/
#include "config.h"
#include "diffcmd.h"
#include <assert.h>
/*
** This function implements a cross-platform "system()" interface.
*/
int portable_system(const char *zOrigCmd){
int rc;
#if defined(_WIN32)
/* On windows, we have to put double-quotes around the entire command.
** Who knows why - this is just the way windows works.
*/
char *zNewCmd = mprintf("\"%s\"", zOrigCmd);
rc = system(zNewCmd);
free(zNewCmd);
#else
|
| ︙ | ︙ | |||
71 72 73 74 75 76 77 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the ** command zDiffCmd to do the diffing. */ static void diff_file( Blob *pFile1, /* In memory content to compare from */ const char *zFile2, /* On disk content to compare to */ const char *zName, /* Display name of the file */ | | > | | 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 |
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
*/
static void diff_file(
Blob *pFile1, /* In memory content to compare from */
const char *zFile2, /* On disk content to compare to */
const char *zName, /* Display name of the file */
const char *zDiffCmd, /* Command for comparison */
int ignoreEolWs /* Ignore whitespace at end of lines */
){
if( zDiffCmd==0 ){
Blob out; /* Diff output text */
Blob file2; /* Content of zFile2 */
/* Read content of zFile2 into memory */
blob_zero(&file2);
blob_read_from_file(&file2, zFile2);
/* Compute and output the differences */
blob_zero(&out);
text_diff(pFile1, &file2, &out, 5, ignoreEolWs);
printf("--- %s\n+++ %s\n", zName, zName);
printf("%s\n", blob_str(&out));
/* Release memory resources */
blob_reset(&file2);
blob_reset(&out);
}else{
|
| ︙ | ︙ | |||
134 135 136 137 138 139 140 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the ** command zDiffCmd to do the diffing. */ static void diff_file_mem( Blob *pFile1, /* In memory content to compare from */ Blob *pFile2, /* In memory content to compare to */ const char *zName, /* Display name of the file */ | | > | | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
*/
static void diff_file_mem(
Blob *pFile1, /* In memory content to compare from */
Blob *pFile2, /* In memory content to compare to */
const char *zName, /* Display name of the file */
const char *zDiffCmd, /* Command for comparison */
int ignoreEolWs /* Ignore whitespace at end of lines */
){
if( zDiffCmd==0 ){
Blob out; /* Diff output text */
blob_zero(&out);
text_diff(pFile1, pFile2, &out, 5, ignoreEolWs);
printf("--- %s\n+++ %s\n", zName, zName);
printf("%s\n", blob_str(&out));
/* Release memory resources */
blob_reset(&out);
}else{
Blob cmd;
|
| ︙ | ︙ | |||
178 179 180 181 182 183 184 | } } /* ** Do a diff against a single file named in g.argv[2] from version zFrom ** against the same file on disk. */ | | > > > > | | > > > > | 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 |
}
}
/*
** Do a diff against a single file named in g.argv[2] from version zFrom
** against the same file on disk.
*/
static void diff_one_against_disk(
const char *zFrom, /* Name of file */
const char *zDiffCmd, /* Use this "diff" command */
int ignoreEolWs /* Ignore whitespace changes at end of lines */
){
Blob fname;
Blob content;
file_tree_name(g.argv[2], &fname, 1);
historical_version_of_file(zFrom, blob_str(&fname), &content, 0);
diff_file(&content, g.argv[2], g.argv[2], zDiffCmd, ignoreEolWs);
blob_reset(&content);
blob_reset(&fname);
}
/*
** Run a diff between the version zFrom and files on disk. zFrom might
** be NULL which means to simply show the difference between the edited
** files on disk and the check-out on which they are based.
*/
static void diff_all_against_disk(
const char *zFrom, /* Version to difference from */
const char *zDiffCmd, /* Use this diff command. NULL for built-in */
int ignoreEolWs /* Ignore end-of-line whitespace */
){
int vid;
Blob sql;
Stmt q;
vid = db_lget_int("checkout", 0);
vfile_check_signature(vid, 1);
blob_zero(&sql);
|
| ︙ | ︙ | |||
263 264 265 266 267 268 269 |
int srcid = db_column_int(&q, 4);
Blob content;
content_get(srcid, &content);
printf("Index: %s\n======================================="
"============================\n",
zPathname
);
| | | > | | > | 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 |
int srcid = db_column_int(&q, 4);
Blob content;
content_get(srcid, &content);
printf("Index: %s\n======================================="
"============================\n",
zPathname
);
diff_file(&content, zFullName, zPathname, zDiffCmd, ignoreEolWs);
blob_reset(&content);
}
free(zFullName);
}
db_finalize(&q);
db_end_transaction(1); /* ROLLBACK */
}
/*
** Output the differences between two versions of a single file.
** zFrom and zTo are the check-ins containing the two file versions.
** The filename is contained in g.argv[2].
*/
static void diff_one_two_versions(
const char *zFrom,
const char *zTo,
const char *zDiffCmd,
int ignoreEolWs
){
char *zName;
Blob fname;
Blob v1, v2;
file_tree_name(g.argv[2], &fname, 1);
zName = blob_str(&fname);
historical_version_of_file(zFrom, zName, &v1, 0);
historical_version_of_file(zTo, zName, &v2, 0);
diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs);
blob_reset(&v1);
blob_reset(&v2);
blob_reset(&fname);
}
/*
** Output the differences between two check-ins.
*/
static void diff_all_two_versions(
const char *zFrom,
const char *zTo,
const char *zDiffCmd,
int ignoreEolWs
){
Manifest mFrom, mTo;
int iFrom, iTo;
manifest_from_name(zFrom, &mFrom);
manifest_from_name(zTo, &mTo);
iFrom = iTo = 0;
|
| ︙ | ︙ | |||
332 333 334 335 336 337 338 339 340 341 342 |
/* No changes */
iFrom++;
iTo++;
}else{
Blob f1, f2;
int rid;
printf("CHANGED %s\n", mFrom.aFile[iFrom].zName);
rid = uuid_to_rid(mFrom.aFile[iFrom].zUuid, 0);
content_get(rid, &f1);
rid = uuid_to_rid(mTo.aFile[iTo].zUuid, 0);
content_get(rid, &f2);
| > > > > | | 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
/* No changes */
iFrom++;
iTo++;
}else{
Blob f1, f2;
int rid;
printf("CHANGED %s\n", mFrom.aFile[iFrom].zName);
printf("Index: %s\n======================================="
"============================\n",
mFrom.aFile[iFrom].zName
);
rid = uuid_to_rid(mFrom.aFile[iFrom].zUuid, 0);
content_get(rid, &f1);
rid = uuid_to_rid(mTo.aFile[iTo].zUuid, 0);
content_get(rid, &f2);
diff_file_mem(&f1, &f2, mFrom.aFile[iFrom].zName, zDiffCmd, ignoreEolWs);
blob_reset(&f1);
blob_reset(&f2);
iFrom++;
iTo++;
}
}
manifest_clear(&mFrom);
|
| ︙ | ︙ | |||
387 388 389 390 391 392 393 |
isInternDiff = find_option("internal","i",0)!=0;
zFrom = find_option("from", "r", 1);
zTo = find_option("to", 0, 1);
if( zTo==0 ){
db_must_be_within_tree();
verify_all_options();
| | | | | | | | 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 |
isInternDiff = find_option("internal","i",0)!=0;
zFrom = find_option("from", "r", 1);
zTo = find_option("to", 0, 1);
if( zTo==0 ){
db_must_be_within_tree();
verify_all_options();
if( !isInternDiff ){
zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
}
if( g.argc==3 ){
diff_one_against_disk(zFrom, zDiffCmd, 0);
}else{
diff_all_against_disk(zFrom, zDiffCmd, 0);
}
}else if( zFrom==0 ){
fossil_fatal("must use --from if --to is present");
}else{
db_find_and_open_repository(1);
verify_all_options();
if( !isInternDiff ){
zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
}
if( g.argc==3 ){
diff_one_two_versions(zFrom, zTo, zDiffCmd, 0);
}else{
diff_all_two_versions(zFrom, zTo, zDiffCmd, 0);
}
}
}
|
Changes to src/encode.c.
| ︙ | ︙ | |||
98 99 100 101 102 103 104 |
static char *EncodeHttp(const char *zIn, int n, int encodeSlash){
int c;
int i = 0;
int count = 0;
char *zOut;
int other;
# define IsSafeChar(X) \
| | > | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
static char *EncodeHttp(const char *zIn, int n, int encodeSlash){
int c;
int i = 0;
int count = 0;
char *zOut;
int other;
# define IsSafeChar(X) \
(isalnum(X) || (X)=='.' || (X)=='$' \
|| (X)=='~' || (X)=='-' || (X)=='_' || (X)==other)
if( zIn==0 ) return 0;
if( n<0 ) n = strlen(zIn);
other = encodeSlash ? 'a' : '/';
while( i<n && (c = zIn[i])!=0 ){
if( IsSafeChar(c) || c==' ' ){
count++;
|
| ︙ | ︙ |
Changes to src/file.c.
| ︙ | ︙ | |||
81 82 83 84 85 86 87 |
/*
** Return TRUE if the named file is an executable. Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_isexe(const char *zFilename){
if( getStat(zFilename) || !S_ISREG(fileStat.st_mode) ) return 0;
| | > > > | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
/*
** Return TRUE if the named file is an executable. Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_isexe(const char *zFilename){
if( getStat(zFilename) || !S_ISREG(fileStat.st_mode) ) return 0;
#if defined(_WIN32)
# if defined(__DMC__) || defined(_MSC_VER)
# define S_IXUSR _S_IEXEC
# endif
return ((S_IXUSR)&fileStat.st_mode)!=0;
#else
return ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0;
#endif
}
|
| ︙ | ︙ | |||
143 144 145 146 147 148 149 |
fclose(out);
}
/*
** Set or clear the execute bit on a file.
*/
void file_setexe(const char *zFilename, int onoff){
| | | | | 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 |
fclose(out);
}
/*
** Set or clear the execute bit on a file.
*/
void file_setexe(const char *zFilename, int onoff){
#if !defined(_WIN32)
struct stat buf;
if( stat(zFilename, &buf)!=0 ) return;
if( onoff ){
if( (buf.st_mode & 0111)!=0111 ){
chmod(zFilename, buf.st_mode | 0111);
}
}else{
if( (buf.st_mode & 0111)!=0 ){
chmod(zFilename, buf.st_mode & ~0111);
}
}
#endif /* _WIN32 */
}
/*
** Create the directory named in the argument, if it does not already
** exist. If forceFlag is 1, delete any prior non-directory object
** with the same name.
**
** Return the number of errors.
*/
int file_mkdir(const char *zName, int forceFlag){
int rc = file_isdir(zName);
if( rc==2 ){
if( !forceFlag ) return 1;
unlink(zName);
}
if( rc!=1 ){
#if defined(_WIN32)
return mkdir(zName);
#else
return mkdir(zName, 0755);
#endif
}
return 0;
}
|
| ︙ | ︙ | |||
229 230 231 232 233 234 235 |
** * removing /A/../
**
** Changes are made in-place. Return the new name length.
*/
int file_simplify_name(char *z, int n){
int i, j;
if( n<0 ) n = strlen(z);
| | | 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
** * removing /A/../
**
** Changes are made in-place. Return the new name length.
*/
int file_simplify_name(char *z, int n){
int i, j;
if( n<0 ) n = strlen(z);
#if defined(_WIN32)
for(i=0; i<n; i++){
if( z[i]=='\\' ) z[i] = '/';
}
#endif
while( n>1 && z[n-1]=='/' ){ n--; }
for(i=j=0; i<n; i++){
if( z[i]=='/' ){
|
| ︙ | ︙ | |||
264 265 266 267 268 269 270 |
** Make the name absolute if it is relative.
** Remove redundant / characters
** Remove all /./ path elements.
** Convert /A/../ to just /
*/
void file_canonical_name(const char *zOrigName, Blob *pOut){
if( zOrigName[0]=='/'
| | | 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
** Make the name absolute if it is relative.
** Remove redundant / characters
** Remove all /./ path elements.
** Convert /A/../ to just /
*/
void file_canonical_name(const char *zOrigName, Blob *pOut){
if( zOrigName[0]=='/'
#if defined(_WIN32)
|| zOrigName[0]=='\\'
|| (strlen(zOrigName)>3 && zOrigName[1]==':'
&& (zOrigName[2]=='\\' || zOrigName[2]=='/'))
#endif
){
blob_set(pOut, zOrigName);
blob_materialize(pOut);
|
| ︙ | ︙ | |||
309 310 311 312 313 314 315 |
**
** Canonical names are full pathnames using "/" not "\" and which
** contain no "/./" or "/../" terms.
*/
int file_is_canonical(const char *z){
int i;
if( z[0]!='/'
| | | 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
**
** Canonical names are full pathnames using "/" not "\" and which
** contain no "/./" or "/../" terms.
*/
int file_is_canonical(const char *z){
int i;
if( z[0]!='/'
#if defined(_WIN32)
&& (z[0]==0 || z[1]!=':' || z[2]!='/')
#endif
) return 0;
for(i=0; z[i]; i++){
if( z[i]=='\\' ) return 0;
if( z[i]=='/' ){
|
| ︙ | ︙ |
Changes to src/finfo.c.
| ︙ | ︙ | |||
134 135 136 137 138 139 140 | blob_zero(&title); blob_appendf(&title, "History of "); hyperlinked_path(zFilename, &title); @ <h2>%b(&title)</h2> blob_reset(&title); pGraph = graph_init(); @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> | | | | | | | > | | | > | 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 |
blob_zero(&title);
blob_appendf(&title, "History of ");
hyperlinked_path(zFilename, &title);
@ <h2>%b(&title)</h2>
blob_reset(&title);
pGraph = graph_init();
@ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
@ <table class="timelineTable">
while( db_step(&q)==SQLITE_ROW ){
const char *zDate = db_column_text(&q, 0);
const char *zCom = db_column_text(&q, 1);
const char *zUser = db_column_text(&q, 2);
int fpid = db_column_int(&q, 3);
int frid = db_column_int(&q, 4);
const char *zPUuid = db_column_text(&q, 5);
const char *zUuid = db_column_text(&q, 6);
const char *zCkin = db_column_text(&q,7);
const char *zBgClr = db_column_text(&q, 8);
const char *zBr = db_column_text(&q, 9);
int gidx;
char zTime[10];
char zShort[20];
char zShortCkin[20];
if( zBr==0 ) zBr = "trunk";
gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr);
if( memcmp(zDate, zPrevDate, 10) ){
sprintf(zPrevDate, "%.10s", zDate);
@ <tr><td>
@ <div class="divider">%s(zPrevDate)</div>
@ </td></tr>
}
memcpy(zTime, &zDate[11], 5);
zTime[5] = 0;
@ <tr><td class="timelineTime">
@ <a href="%s(g.zTop)/timeline?c=%t(zDate)">%s(zTime)</a></td>
@ <td class="timelineGraph"><div id="m%d(gidx)"></div></td>
if( zBgClr && zBgClr[0] ){
@ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
}else{
@ <td class="timelineTableCell">
}
sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid);
sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin);
if( zUuid ){
if( g.okHistory ){
@ <a href="%s(g.zTop)/artifact/%s(zUuid)">[%S(zUuid)]</a>
}else{
@ [%S(zUuid)]
}
@ part of check-in
}else{
@ <b>Deleted</b> by check-in
}
hyperlink_to_uuid(zShortCkin);
@ %h(zCom) (user:
hyperlink_to_user(zUser, zDate, "");
@ branch: %h(zBr))
if( g.okHistory && zUuid ){
const char *z = zFilename;
if( fpid ){
@ <a href="%s(g.zTop)/fdiff?v1=%s(zPUuid)&v2=%s(zUuid)">[diff]</a>
}
@ <a href="%s(g.zTop)/annotate?checkin=%S(zCkin)&filename=%h(z)">
@ [annotate]</a>
}
@ </td></tr>
}
db_finalize(&q);
if( pGraph ){
graph_finish(pGraph, 1);
if( pGraph->nErr ){
graph_free(pGraph);
pGraph = 0;
}else{
@ <tr><td></td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
@ </td></tr>
}
}
@ </table>
timeline_output_graph_javascript(pGraph);
style_footer();
}
|
Changes to src/graph.c.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 | */ #include "config.h" #include "graph.h" #include <assert.h> #if INTERFACE | | | | > | | | | | | < | | 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 |
*/
#include "config.h"
#include "graph.h"
#include <assert.h>
#if INTERFACE
#define GR_MAX_PARENT 10 /* Most parents for any one node */
#define GR_MAX_RAIL 32 /* Max number of "rails" to display */
/* The graph appears vertically beside a timeline. Each row in the
** timeline corresponds to a row in the graph.
*/
struct GraphRow {
int rid; /* The rid for the check-in */
int nParent; /* Number of parents */
int aParent[GR_MAX_PARENT]; /* Array of parents. 0 element is primary .*/
char *zBranch; /* Branch name */
char *zBgClr; /* Background Color */
GraphRow *pNext; /* Next row down in the list of all rows */
GraphRow *pPrev; /* Previous row */
int idx; /* Row index. First is 1. 0 used for "none" */
int idxTop; /* Direct descendent highest up on the graph */
GraphRow *pChild; /* Child immediately above this node */
u8 isDup; /* True if this is duplicate of a prior entry */
int iRail; /* Which rail this check-in appears on. 0-based.*/
int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */
int bDescender; /* Raiser from bottom of graph to here. */
u32 mergeIn; /* Merge in from other rails */
int mergeOut; /* Merge out to this rail */
int mergeUpto; /* Draw the merge rail up to this level */
u32 railInUse; /* Mask of occupied rails */
};
/* Context while building a graph
*/
struct GraphContext {
int nErr; /* Number of errors encountered */
int mxRail; /* Number of rails required to render the graph */
GraphRow *pFirst; /* First row in the list */
GraphRow *pLast; /* Last row in the list */
int nBranch; /* Number of distinct branches */
char **azBranch; /* Names of the branches */
int nRow; /* Number of rows */
int nHash; /* Number of slots in apHash[] */
GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */
};
#endif
/*
** Malloc for zeroed space. Panic if unable to provide the
** requested space.
|
| ︙ | ︙ | |||
101 102 103 104 105 106 107 | for(i=0; i<p->nBranch; i++) free(p->azBranch[i]); free(p->azBranch); free(p->apHash); free(p); } /* | | | | | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
for(i=0; i<p->nBranch; i++) free(p->azBranch[i]);
free(p->azBranch);
free(p->apHash);
free(p);
}
/*
** Insert a row into the hash table. pRow->rid is the key. Keys must
** be unique. If there is already another row with the same rid,
** overwrite the prior entry if and only if the overwrite flag is set.
*/
static void hashInsert(GraphContext *p, GraphRow *pRow, int overwrite){
int h;
h = pRow->rid % p->nHash;
while( p->apHash[h] && p->apHash[h]->rid!=pRow->rid ){
h++;
if( h>=p->nHash ) h = 0;
|
| ︙ | ︙ | |||
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
return p->apHash[h];
}
/*
** Return the canonical pointer for a given branch name.
** Multiple calls to this routine with equivalent strings
** will return the same pointer.
**
** Note: also used for background color names.
*/
static char *persistBranchName(GraphContext *p, const char *zBranch){
int i;
for(i=0; i<p->nBranch; i++){
if( strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i];
}
p->nBranch++;
p->azBranch = realloc(p->azBranch, sizeof(char*)*p->nBranch);
if( p->azBranch==0 ) fossil_panic("out of memory");
p->azBranch[p->nBranch-1] = mprintf("%s", zBranch);
return p->azBranch[p->nBranch-1];
}
/*
| > > > > | | 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 |
return p->apHash[h];
}
/*
** Return the canonical pointer for a given branch name.
** Multiple calls to this routine with equivalent strings
** will return the same pointer.
**
** The returned value is a pointer to a (readonly) string that
** has the useful property that strings can be checked for
** equality by comparing pointers.
**
** Note: also used for background color names.
*/
static char *persistBranchName(GraphContext *p, const char *zBranch){
int i;
for(i=0; i<p->nBranch; i++){
if( strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i];
}
p->nBranch++;
p->azBranch = realloc(p->azBranch, sizeof(char*)*p->nBranch);
if( p->azBranch==0 ) fossil_panic("out of memory");
p->azBranch[p->nBranch-1] = mprintf("%s", zBranch);
return p->azBranch[p->nBranch-1];
}
/*
** Add a new row to the graph context. Rows are added from top to bottom.
*/
int graph_add_row(
GraphContext *p, /* The context to which the row is added */
int rid, /* RID for the check-in */
int nParent, /* Number of parents */
int *aParent, /* Array of parents */
const char *zBranch, /* Branch for this check-in */
|
| ︙ | ︙ | |||
177 178 179 180 181 182 183 |
if( p->pFirst==0 ){
p->pFirst = pRow;
}else{
p->pLast->pNext = pRow;
}
p->pLast = pRow;
p->nRow++;
| | | 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
if( p->pFirst==0 ){
p->pFirst = pRow;
}else{
p->pLast->pNext = pRow;
}
p->pLast = pRow;
p->nRow++;
pRow->idx = pRow->idxTop = p->nRow;
return pRow->idx;
}
/*
** Return the index of a rail currently not in use for any row between
** top and bottom, inclusive.
*/
|
| ︙ | ︙ | |||
217 218 219 220 221 222 223 224 225 226 227 228 |
iBest = i;
}
}
}
if( iBestDist>1000 ) p->nErr++;
return iBest;
}
/*
** Compute the complete graph
*/
void graph_finish(GraphContext *p, int omitDescenders){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 |
iBest = i;
}
}
}
if( iBestDist>1000 ) p->nErr++;
return iBest;
}
/*
** Assign all children of node pBottom to the same rail as pBottom.
*/
static void assignChildrenToRail(GraphRow *pBottom){
int iRail = pBottom->iRail;
GraphRow *pCurrent;
GraphRow *pPrior;
u32 mask = 1<<iRail;
pBottom->iRail = iRail;
pBottom->railInUse |= mask;
pPrior = pBottom;
for(pCurrent=pBottom->pChild; pCurrent; pCurrent=pCurrent->pChild){
assert( pPrior->idx > pCurrent->idx );
assert( pCurrent->iRail<0 );
pCurrent->iRail = iRail;
pCurrent->railInUse |= mask;
pPrior->aiRaiser[iRail] = pCurrent->idx;
while( pPrior->idx > pCurrent->idx ){
pPrior->railInUse |= mask;
pPrior = pPrior->pPrev;
assert( pPrior!=0 );
}
if( pCurrent->pPrev ){
pCurrent->pPrev->railInUse |= mask;
}
}
}
/*
** Compute the complete graph
*/
void graph_finish(GraphContext *p, int omitDescenders){
GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent;
int i;
u32 mask;
u32 inUse;
int hasDup = 0; /* True if one or more isDup entries */
const char *zTrunk;
if( p==0 || p->pFirst==0 || p->nErr ) return;
|
| ︙ | ︙ | |||
246 247 248 249 250 251 252 |
hasDup = 1;
pDup->isDup = 1;
}
hashInsert(p, pRow, 1);
}
p->mxRail = -1;
| | > > > > > > > > > | > > > | < < | < < | | | > | > | | > > > | | < < < < | | > < < > | > > > > | | < < < | < | > > | > > | | | < > | | | | 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 |
hasDup = 1;
pDup->isDup = 1;
}
hashInsert(p, pRow, 1);
}
p->mxRail = -1;
/* Purge merge-parents that are out-of-graph.
**
** Each node has one primary parent and zero or more "merge" parents.
** A merge parent is a prior checkin from which changes were merged into
** the current check-in. If a merge parent is not in the visible section
** of this graph, then no arrows will be drawn for it, so remove it from
** the aParent[] array.
*/
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
for(i=1; i<pRow->nParent; i++){
if( hashFind(p, pRow->aParent[i])==0 ){
pRow->aParent[i] = pRow->aParent[--pRow->nParent];
i--;
}
}
}
/* Find the pChild pointer for each node.
**
** The pChild points to node directly above on the same rail.
** The pChild must be in the same branch. Leaf nodes have a NULL
** pChild.
**
** In the case of a fork, choose the pChild that results in the
** longest rail.
*/
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->isDup ) continue;
if( pRow->nParent==0 ) continue;
pParent = hashFind(p, pRow->aParent[0]);
if( pParent==0 ) continue;
if( pParent->zBranch!=pRow->zBranch ) continue;
if( pParent->idx <= pRow->idx ) continue;
if( pRow->idxTop < pParent->idxTop ){
pParent->pChild = pRow;
pParent->idxTop = pRow->idxTop;
}
}
/* Identify rows where the primary parent is off screen. Assign
** each to a rail and draw descenders to the bottom of the screen.
**
** Strive to put the "trunk" branch on far left.
*/
zTrunk = persistBranchName(p, "trunk");
for(i=0; i<2; i++){
for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
if( i==0 ){
if( pRow->zBranch!=zTrunk ) continue;
}else {
if( pRow->iRail>=0 ) continue;
}
if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
if( omitDescenders ){
pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx, 0, 0);
}else{
pRow->iRail = ++p->mxRail;
}
mask = 1<<(pRow->iRail);
if( omitDescenders ){
if( pRow->pNext ) pRow->pNext->railInUse |= mask;
}else{
pRow->bDescender = pRow->nParent>0;
for(pLoop=pRow; pLoop; pLoop=pLoop->pNext){
pLoop->railInUse |= mask;
}
}
assignChildrenToRail(pRow);
}
}
}
/* Assign rails to all rows that are still unassigned.
*/
inUse = (1<<(p->mxRail+1))-1;
for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
int parentRid;
if( pRow->iRail>=0 ){
if( pRow->pChild==0 ) inUse &= ~(1<<pRow->iRail);
continue;
}
if( pRow->isDup ){
pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, inUse, 0);
pDesc = pRow;
pParent = 0;
}else{
assert( pRow->nParent>0 );
parentRid = pRow->aParent[0];
pParent = hashFind(p, parentRid);
if( pParent==0 ){
/* Time skew */
pRow->iRail = ++p->mxRail;
pRow->railInUse = 1<<pRow->iRail;
continue;
}
pRow->iRail = findFreeRail(p, 0, pParent->idx, inUse, pParent->iRail);
pParent->aiRaiser[pRow->iRail] = pRow->idx;
}
mask = 1<<pRow->iRail;
if( pRow->pPrev ) pRow->pPrev->railInUse |= mask;
if( pRow->pNext ) pRow->pNext->railInUse |= mask;
if( pRow->pChild==0 ){
inUse &= ~mask;
}else{
inUse |= mask;
assignChildrenToRail(pRow);
}
if( pParent ){
for(pLoop=pParent; pLoop && pLoop!=pRow; pLoop=pLoop->pPrev){
pLoop->railInUse |= mask;
}
}
}
/*
** Insert merge rails and merge arrows
*/
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
for(i=1; i<pRow->nParent; i++){
int parentRid = pRow->aParent[i];
pDesc = hashFind(p, parentRid);
if( pDesc==0 ) continue;
if( pDesc->mergeOut<0 ){
int iTarget = (pRow->iRail + pDesc->iRail)/2;
pDesc->mergeOut = findFreeRail(p, pRow->idx, pDesc->idx, 0, iTarget);
pDesc->mergeUpto = pRow->idx;
mask = 1<<pDesc->mergeOut;
pDesc->railInUse |= mask;
for(pLoop=pRow->pNext; pLoop && pLoop->rid!=parentRid;
pLoop=pLoop->pNext){
pLoop->railInUse |= mask;
}
}
pRow->mergeIn |= 1<<pDesc->mergeOut;
}
}
/*
|
| ︙ | ︙ | |||
396 397 398 399 400 401 402 |
pRow->mergeIn |= 1<<pDesc->mergeOut;
}
}
/*
** Find the maximum rail number.
*/
| < | 443 444 445 446 447 448 449 450 451 452 453 454 455 |
pRow->mergeIn |= 1<<pDesc->mergeOut;
}
}
/*
** Find the maximum rail number.
*/
p->mxRail = 0;
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
}
}
|
Changes to src/http.c.
| ︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
Blob pw; /* The nonce with user password appended */
Blob sig; /* The signature field */
blob_zero(pLogin);
if( g.urlUser==0 || strcmp(g.urlUser, "anonymous")==0 ){
return; /* If no login card for users "nobody" and "anonymous" */
}
blob_zero(&nonce);
blob_zero(&pw);
sha1sum_blob(pPayload, &nonce);
blob_copy(&pw, &nonce);
zLogin = g.urlUser;
if( g.urlPasswd ){
zPw = g.urlPasswd;
| > > > | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
Blob pw; /* The nonce with user password appended */
Blob sig; /* The signature field */
blob_zero(pLogin);
if( g.urlUser==0 || strcmp(g.urlUser, "anonymous")==0 ){
return; /* If no login card for users "nobody" and "anonymous" */
}
if( g.urlIsSsh ){
return; /* If no login card for SSH: */
}
blob_zero(&nonce);
blob_zero(&pw);
sha1sum_blob(pPayload, &nonce);
blob_copy(&pw, &nonce);
zLogin = g.urlUser;
if( g.urlPasswd ){
zPw = g.urlPasswd;
|
| ︙ | ︙ | |||
132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
Blob hdr; /* The HTTP request header */
int closeConnection; /* True to close the connection when done */
int iLength; /* Length of the reply payload */
int rc; /* Result code */
int iHttpVersion; /* Which version of HTTP protocol server uses */
char *zLine; /* A single line of the reply header */
int i; /* Loop counter */
if( transport_open() ){
fossil_fatal(transport_errmsg());
}
/* Construct the login card and prepare the complete payload */
blob_zero(&login);
| > | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
Blob hdr; /* The HTTP request header */
int closeConnection; /* True to close the connection when done */
int iLength; /* Length of the reply payload */
int rc; /* Result code */
int iHttpVersion; /* Which version of HTTP protocol server uses */
char *zLine; /* A single line of the reply header */
int i; /* Loop counter */
int isError = 0; /* True if the reply is an error message */
if( transport_open() ){
fossil_fatal(transport_errmsg());
}
/* Construct the login card and prepare the complete payload */
blob_zero(&login);
|
| ︙ | ︙ | |||
188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
/*
** Read and interpret the server reply
*/
closeConnection = 1;
iLength = -1;
while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){
if( strncasecmp(zLine, "http/1.", 7)==0 ){
if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err;
if( rc!=200 && rc!=302 ){
int ii;
for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){}
while( zLine[ii]==' ' ) ii++;
fossil_fatal("server says: %s\n", &zLine[ii]);
| > | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
/*
** Read and interpret the server reply
*/
closeConnection = 1;
iLength = -1;
while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){
/* printf("[%s]\n", zLine); fflush(stdout); */
if( strncasecmp(zLine, "http/1.", 7)==0 ){
if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err;
if( rc!=200 && rc!=302 ){
int ii;
for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){}
while( zLine[ii]==' ' ) ii++;
fossil_fatal("server says: %s\n", &zLine[ii]);
|
| ︙ | ︙ | |||
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 |
j = strlen(zLine) - 1;
if( j>4 && strcmp(&zLine[j-4],"/xfer")==0 ) zLine[j-4] = 0;
fossil_print("redirect to %s\n", &zLine[i]);
url_parse(&zLine[i]);
transport_close();
http_exchange(pSend, pReply, useLogin);
return;
}
}
if( rc!=200 ){
fossil_fatal("\"location:\" missing from 302 redirect reply");
goto write_err;
}
/*
** Extract the reply payload that follows the header
*/
if( iLength<0 ){
fossil_fatal("server did not reply");
goto write_err;
}
blob_zero(pReply);
blob_resize(pReply, iLength);
iLength = transport_receive(blob_buffer(pReply), iLength);
blob_resize(pReply, iLength);
if( g.fHttpTrace ){
printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pReply));
}else{
blob_uncompress(pReply, pReply);
}
/*
| > > > > > > > > > > > > > > > > | 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 |
j = strlen(zLine) - 1;
if( j>4 && strcmp(&zLine[j-4],"/xfer")==0 ) zLine[j-4] = 0;
fossil_print("redirect to %s\n", &zLine[i]);
url_parse(&zLine[i]);
transport_close();
http_exchange(pSend, pReply, useLogin);
return;
}else if( strncasecmp(zLine, "content-type: text/html", 23)==0 ){
isError = 1;
}
}
if( rc!=200 ){
fossil_fatal("\"location:\" missing from 302 redirect reply");
goto write_err;
}
/*
** Extract the reply payload that follows the header
*/
if( iLength<0 ){
fossil_fatal("server did not reply");
goto write_err;
}
blob_zero(pReply);
blob_resize(pReply, iLength);
iLength = transport_receive(blob_buffer(pReply), iLength);
blob_resize(pReply, iLength);
if( isError ){
char *z;
int i, j;
z = blob_str(pReply);
for(i=j=0; z[i]; i++, j++){
if( z[i]=='<' ){
while( z[i] && z[i]!='>' ) i++;
if( z[i]==0 ) break;
}
z[j] = z[i];
}
z[j] = 0;
fossil_fatal("server sends error: %s", z);
}
if( g.fHttpTrace ){
printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pReply));
}else{
blob_uncompress(pReply, pReply);
}
/*
|
| ︙ | ︙ |
Changes to src/http_socket.c.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 | ** ** Low-level sockets are abstracted out into this module because they ** are handled different on Unix and windows. */ #include "config.h" #include "http_socket.h" | | | > > | > | | 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 | ** ** Low-level sockets are abstracted out into this module because they ** are handled different on Unix and windows. */ #include "config.h" #include "http_socket.h" #if defined(_WIN32) # include <windows.h> /* for Sleep once server works again */ # define sleep Sleep /* windows does not have sleep, but Sleep */ # if defined(__MINGW32__) # include <ws2tcpip.h> # endif #else # include <arpa/inet.h> # include <sys/socket.h> # include <netdb.h> # include <netinet/in.h> #endif #include <assert.h> #include <sys/types.h> #include <signal.h> /* ** There can only be a single socket connection open at a time. ** State information about that socket is stored in the following ** local variables: */ static int socketIsInit = 0; /* True after global initialization */ #if defined(_WIN32) static WSADATA socketInfo; /* Windows socket initialize data */ #endif static int iSocket = -1; /* The socket on which we talk to the server */ static char *socketErrMsg = 0; /* Text of most recent socket error */ /* |
| ︙ | ︙ | |||
82 83 84 85 86 87 88 |
/*
** Call this routine once before any other use of the socket interface.
** This routine does initial configuration of the socket module.
*/
void socket_global_init(void){
if( socketIsInit==0 ){
| | | | | 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 |
/*
** Call this routine once before any other use of the socket interface.
** This routine does initial configuration of the socket module.
*/
void socket_global_init(void){
if( socketIsInit==0 ){
#if defined(_WIN32)
if( WSAStartup(MAKEWORD(2,0), &socketInfo)!=0 ){
fossil_panic("can't initialize winsock");
}
#endif
socketIsInit = 1;
}
}
/*
** Call this routine to shutdown the socket module prior to program
** exit.
*/
void socket_global_shutdown(void){
if( socketIsInit ){
#if defined(_WIN32)
WSACleanup();
#endif
socket_clear_errmsg();
socketIsInit = 0;
}
}
/*
** Close the currently open socket. If no socket is open, this routine
** is a no-op.
*/
void socket_close(void){
if( iSocket>=0 ){
#if defined(_WIN32)
closesocket(iSocket);
#else
close(iSocket);
#endif
iSocket = -1;
}
}
|
| ︙ | ︙ | |||
169 170 171 172 173 174 175 |
return 1;
}
if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){
socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort);
socket_close();
return 1;
}
| | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
return 1;
}
if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){
socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort);
socket_close();
return 1;
}
#if !defined(_WIN32)
signal(SIGPIPE, SIG_IGN);
#endif
return 0;
}
/*
** Send content out over the open socket connection.
|
| ︙ | ︙ |
Changes to src/http_ssl.c.
| ︙ | ︙ | |||
126 127 128 129 130 131 132 |
** g.urlPort TCP/IP port to use. Ex: 80
**
** Return the number of errors.
*/
int ssl_open(void){
X509 *cert;
int hasSavedCertificate = 0;
| | | 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
** g.urlPort TCP/IP port to use. Ex: 80
**
** Return the number of errors.
*/
int ssl_open(void){
X509 *cert;
int hasSavedCertificate = 0;
char *connStr ;
ssl_global_init();
/* Get certificate for current server from global config and
* (if we have it in config) add it to certificate store.
*/
cert = ssl_get_certificate();
if ( cert!=NULL ){
|
| ︙ | ︙ | |||
148 149 150 151 152 153 154 |
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
if( iBio==NULL ) {
ssl_set_errmsg("SSL: cannot open SSL (%s)",
ERR_reason_error_string(ERR_get_error()));
return 1;
}
| | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
if( iBio==NULL ) {
ssl_set_errmsg("SSL: cannot open SSL (%s)",
ERR_reason_error_string(ERR_get_error()));
return 1;
}
connStr = mprintf("%s:%d", g.urlName, g.urlPort);
BIO_set_conn_hostname(iBio, connStr);
free(connStr);
if( BIO_do_connect(iBio)<=0 ){
ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error()));
ssl_close();
|
| ︙ | ︙ | |||
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
ssl_set_errmsg("No SSL certificate was presented by the peer");
ssl_close();
return 1;
}
if( SSL_get_verify_result(ssl) != X509_V_OK ){
char *desc, *prompt;
BIO *mem;
mem = BIO_new(BIO_s_mem());
X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE);
BIO_puts(mem, "\n\nIssued By:\n\n");
X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE);
BIO_write(mem, "", 1); // null-terminate mem buffer
BIO_get_mem_data(mem, &desc);
| > > < < | 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 |
ssl_set_errmsg("No SSL certificate was presented by the peer");
ssl_close();
return 1;
}
if( SSL_get_verify_result(ssl) != X509_V_OK ){
char *desc, *prompt;
char *warning = "";
Blob ans;
BIO *mem;
mem = BIO_new(BIO_s_mem());
X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE);
BIO_puts(mem, "\n\nIssued By:\n\n");
X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE);
BIO_write(mem, "", 1); // null-terminate mem buffer
BIO_get_mem_data(mem, &desc);
if( hasSavedCertificate ){
warning = "WARNING: Certificate doesn't match the "
"saved certificate for this host!";
}
prompt = mprintf("\nUnknown SSL certificate:\n\n%s\n\n%s\n"
"Accept certificate [a=always/y/N]? ", desc, warning);
BIO_free(mem);
prompt_user(prompt, &ans);
free(prompt);
if( blob_str(&ans)[0]!='y' && blob_str(&ans)[0]!='a' ) {
X509_free(cert);
ssl_set_errmsg("SSL certificate declined");
ssl_close();
return 1;
|
| ︙ | ︙ |
Changes to src/http_transport.c.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
int nRcvd; /* Number of bytes received */
FILE *pFile; /* File I/O for FILE: */
char *zOutFile; /* Name of outbound file for FILE: */
char *zInFile; /* Name of inbound file for FILE: */
} transport = {
0, 0, 0, 0, 0, 0, 0
};
/*
** Return the current transport error message.
*/
const char *transport_errmsg(void){
#ifdef FOSSIL_ENABLE_SSL
if( g.urlIsHttps ){
| > > > > > > > > > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
int nRcvd; /* Number of bytes received */
FILE *pFile; /* File I/O for FILE: */
char *zOutFile; /* Name of outbound file for FILE: */
char *zInFile; /* Name of inbound file for FILE: */
} transport = {
0, 0, 0, 0, 0, 0, 0
};
/*
** Information about the connection to the SSH subprocess when
** using the ssh:// sync method.
*/
static int sshPid; /* Process id of ssh subprocess */
static int sshIn; /* From ssh subprocess to this process */
static FILE *sshOut; /* From this to ssh subprocess */
/*
** Return the current transport error message.
*/
const char *transport_errmsg(void){
#ifdef FOSSIL_ENABLE_SSL
if( g.urlIsHttps ){
|
| ︙ | ︙ | |||
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
if( pnSent ) *pnSent = transport.nSent;
if( pnRcvd ) *pnRcvd = transport.nRcvd;
if( resetFlag ){
transport.nSent = 0;
transport.nRcvd = 0;
}
}
/*
** Open a connection to the server. The server is defined by the following
** global variables:
**
** g.urlName Name of the server. Ex: www.fossil-scm.org
** g.urlPort TCP/IP port. Ex: 80
** g.urlIsHttps Use TLS for the connection
**
** Return the number of errors.
*/
int transport_open(void){
int rc = 0;
if( transport.isOpen==0 ){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | 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 |
if( pnSent ) *pnSent = transport.nSent;
if( pnRcvd ) *pnRcvd = transport.nRcvd;
if( resetFlag ){
transport.nSent = 0;
transport.nRcvd = 0;
}
}
/*
** Read text from sshIn. Zero-terminate and remove trailing
** whitespace.
*/
static void sshin_read(char *zBuf, int szBuf){
int got;
zBuf[0] = 0;
got = read(sshIn, zBuf, szBuf-1);
while( got>=0 ){
zBuf[got] = 0;
if( got==0 || !isspace(zBuf[got-1]) ) break;
got--;
}
}
/*
** Default SSH command
*/
#ifdef __MINGW32__
static char zDefaultSshCmd[] = "ssh -T";
#else
static char zDefaultSshCmd[] = "ssh -e none -T";
#endif
/*
** Global initialization of the transport layer
*/
void transport_global_startup(void){
if( g.urlIsSsh ){
/* Only SSH requires a global initialization. For SSH we need to create
** and run an SSH command to talk to the remote machine.
*/
const char *zSsh; /* The base SSH command */
Blob zCmd; /* The SSH command */
char *zHost; /* The host name to contact */
char zIn[200]; /* An input line received back from remote */
zSsh = db_get("ssh-command", zDefaultSshCmd);
blob_init(&zCmd, zSsh, -1);
if( g.urlPort!=g.urlDfltPort ){
#ifdef __MINGW32__
blob_appendf(&zCmd, " -P %d", g.urlPort);
#else
blob_appendf(&zCmd, " -p %d", g.urlPort);
#endif
}
if( g.urlUser && g.urlUser[0] ){
zHost = mprintf("%s@%s", g.urlUser, g.urlName);
#ifdef __MINGW32__
/* Only win32 (and specifically PLINK.EXE support the -pw option */
if( g.urlPasswd && g.urlPasswd[0] ){
Blob pw;
blob_zero(&pw);
if( g.urlPasswd[0]=='*' ){
char *zPrompt;
zPrompt = mprintf("Password for [%s]: ", zHost);
prompt_for_password(zPrompt, &pw, 0);
free(zPrompt);
}else{
blob_init(&pw, g.urlPasswd, -1);
}
blob_append(&zCmd, " -pw ", -1);
shell_escape(&zCmd, blob_str(&pw));
blob_reset(&pw);
}
#endif
}else{
zHost = mprintf("%s", g.urlName);
}
blob_append(&zCmd, " ", 1);
shell_escape(&zCmd, zHost);
free(zHost);
/* printf("%s\n", blob_str(&zCmd)); */
popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
if( sshPid==0 ){
fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd);
}
blob_reset(&zCmd);
/* Send an "echo" command to the other side to make sure that the
** connection is up and working.
*/
fprintf(sshOut, "echo test\n");
fflush(sshOut);
sshin_read(zIn, sizeof(zIn));
if( memcmp(zIn, "test", 4)!=0 ){
pclose2(sshIn, sshOut, sshPid);
fossil_fatal("ssh connection failed: [%s]", zIn);
}
}
}
/*
** Open a connection to the server. The server is defined by the following
** global variables:
**
** g.urlName Name of the server. Ex: www.fossil-scm.org
** g.urlPort TCP/IP port. Ex: 80
** g.urlIsHttps Use TLS for the connection
**
** Return the number of errors.
*/
int transport_open(void){
int rc = 0;
if( transport.isOpen==0 ){
if( g.urlIsSsh ){
Blob cmd;
blob_zero(&cmd);
shell_escape(&cmd, g.urlFossil);
blob_append(&cmd, " test-http ", -1);
shell_escape(&cmd, g.urlPath);
/* fprintf(stdout, "%s\n", blob_str(&cmd)); */
fprintf(sshOut, "%s\n", blob_str(&cmd));
fflush(sshOut);
blob_reset(&cmd);
}else if( g.urlIsHttps ){
#ifdef FOSSIL_ENABLE_SSL
rc = ssl_open();
if( rc==0 ) transport.isOpen = 1;
#else
socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support");
rc = 1;
#endif
|
| ︙ | ︙ | |||
113 114 115 116 117 118 119 |
void transport_close(void){
if( transport.isOpen ){
free(transport.pBuf);
transport.pBuf = 0;
transport.nAlloc = 0;
transport.nUsed = 0;
transport.iCursor = 0;
| | > > | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
void transport_close(void){
if( transport.isOpen ){
free(transport.pBuf);
transport.pBuf = 0;
transport.nAlloc = 0;
transport.nUsed = 0;
transport.iCursor = 0;
if( g.urlIsSsh ){
/* No-op */
}else if( g.urlIsHttps ){
#ifdef FOSSIL_ENABLE_SSL
ssl_close();
#endif
}else if( g.urlIsFile ){
if( transport.pFile ){
fclose(transport.pFile);
transport.pFile = 0;
|
| ︙ | ︙ | |||
140 141 142 143 144 145 146 |
/*
** Send content over the wire.
*/
void transport_send(Blob *toSend){
char *z = blob_buffer(toSend);
int n = blob_size(toSend);
transport.nSent += n;
| | > > > > > | 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
/*
** Send content over the wire.
*/
void transport_send(Blob *toSend){
char *z = blob_buffer(toSend);
int n = blob_size(toSend);
transport.nSent += n;
if( g.urlIsSsh ){
int sent;
sent = fwrite(z, 1, n, sshOut);
fflush(sshOut);
/* printf("sent %d of %d bytes\n", sent, n); fflush(stdout); */
}else if( g.urlIsHttps ){
#ifdef FOSSIL_ENABLE_SSL
int sent;
while( n>0 ){
sent = ssl_send(0, z, n);
/* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */
if( sent<=0 ) break;
n -= sent;
|
| ︙ | ︙ | |||
168 169 170 171 172 173 174 |
}
/*
** This routine is called when the outbound message is complete and
** it is time to being recieving a reply.
*/
void transport_flip(void){
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < < < < < | 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 |
}
/*
** This routine is called when the outbound message is complete and
** it is time to being recieving a reply.
*/
void transport_flip(void){
if( g.urlIsSsh ){
fprintf(sshOut, "\n\n");
}else if( g.urlIsFile ){
char *zCmd;
fclose(transport.pFile);
zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1",
g.argv[0], g.urlName, transport.zOutFile, transport.zInFile
);
portable_system(zCmd);
free(zCmd);
transport.pFile = fopen(transport.zInFile, "rb");
}
}
/*
** This routine is called when the inbound message has been received
** and it is time to start sending again.
*/
void transport_rewind(void){
if( g.urlIsFile ){
transport_close();
}
}
/*
** Read N bytes of content directly from the wire and write into
** the buffer.
*/
static int transport_fetch(char *zBuf, int N){
int got;
if( sshIn ){
int x;
int wanted = N;
got = 0;
while( wanted>0 ){
x = read(sshIn, &zBuf[got], wanted);
if( x<=0 ) break;
got += x;
wanted -= x;
}
}else if( g.urlIsHttps ){
#ifdef FOSSIL_ENABLE_SSL
got = ssl_receive(0, zBuf, N);
#else
got = 0;
#endif
}else if( g.urlIsFile ){
got = fread(zBuf, 1, N, transport.pFile);
}else{
got = socket_receive(0, zBuf, N);
}
/* printf("received %d of %d bytes\n", got, N); fflush(stdout); */
return got;
}
/*
** Read N bytes of content from the wire and store in the supplied buffer.
** Return the number of bytes actually received.
*/
int transport_receive(char *zBuf, int N){
int onHand; /* Bytes current held in the transport buffer */
int nByte = 0; /* Bytes of content received */
onHand = transport.nUsed - transport.iCursor;
/* printf("request %d with %d on hand\n", N, onHand); fflush(stdout); */
if( onHand>0 ){
int toMove = onHand;
if( toMove>N ) toMove = N;
/* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */
memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove);
transport.iCursor += toMove;
if( transport.iCursor>=transport.nUsed ){
transport.nUsed = 0;
transport.iCursor = 0;
}
N -= toMove;
zBuf += toMove;
nByte += toMove;
}
if( N>0 ){
int got = transport_fetch(zBuf, N);
if( got>0 ){
nByte += got;
transport.nRcvd += got;
}
}
return nByte;
}
|
| ︙ | ︙ | |||
265 266 267 268 269 270 271 |
char *pNew;
transport.nAlloc = transport.nUsed + N;
pNew = realloc(transport.pBuf, transport.nAlloc);
if( pNew==0 ) fossil_panic("out of memory");
transport.pBuf = pNew;
}
if( N>0 ){
| | | 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 |
char *pNew;
transport.nAlloc = transport.nUsed + N;
pNew = realloc(transport.pBuf, transport.nAlloc);
if( pNew==0 ) fossil_panic("out of memory");
transport.pBuf = pNew;
}
if( N>0 ){
i = transport_fetch(&transport.pBuf[transport.nUsed], N);
if( i>0 ){
transport.nUsed += i;
}
}
}
/*
|
| ︙ | ︙ | |||
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
i++;
}
/* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */
return &transport.pBuf[iStart];
}
void transport_global_shutdown(void){
if( g.urlIsHttps ){
#ifdef FOSSIL_ENABLE_SSL
ssl_global_shutdown();
#endif
}else{
socket_global_shutdown();
}
}
| > > > > > > | 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 |
i++;
}
/* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */
return &transport.pBuf[iStart];
}
void transport_global_shutdown(void){
if( g.urlIsSsh && sshPid ){
printf("Closing SSH tunnel: ");
fflush(stdout);
pclose2(sshIn, sshOut, sshPid);
sshPid = 0;
}
if( g.urlIsHttps ){
#ifdef FOSSIL_ENABLE_SSL
ssl_global_shutdown();
#endif
}else{
socket_global_shutdown();
}
}
|
Changes to src/info.c.
| ︙ | ︙ | |||
63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
"SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
rid
);
/* 01234567890123 */
printf("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : "");
free(zUuid);
free(zDate);
}
db_prepare(&q, "SELECT uuid, pid FROM plink JOIN blob ON pid=rid "
" WHERE cid=%d", rid);
while( db_step(&q)==SQLITE_ROW ){
const char *zUuid = db_column_text(&q, 0);
zDate = db_text("",
"SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
| > > > > > > | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
"SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
rid
);
/* 01234567890123 */
printf("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : "");
free(zUuid);
free(zDate);
}
if( zUuid && showComment ){
zComment = db_text(0,
"SELECT coalesce(ecomment,comment) || ' (user: ' || coalesce(euser,user,'?') || ')' FROM event WHERE objid=%d",
rid
);
}
db_prepare(&q, "SELECT uuid, pid FROM plink JOIN blob ON pid=rid "
" WHERE cid=%d", rid);
while( db_step(&q)==SQLITE_ROW ){
const char *zUuid = db_column_text(&q, 0);
zDate = db_text("",
"SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
|
| ︙ | ︙ | |||
94 95 96 97 98 99 100 |
db_finalize(&q);
zTags = info_tags_of_checkin(rid, 0);
if( zTags && zTags[0] ){
printf("tags: %s\n", zTags);
}
free(zTags);
if( zComment ){
| | > | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
db_finalize(&q);
zTags = info_tags_of_checkin(rid, 0);
if( zTags && zTags[0] ){
printf("tags: %s\n", zTags);
}
free(zTags);
if( zComment ){
printf("comment: ");
comment_print(zComment, 14, 79);
free(zComment);
}
}
/*
** COMMAND: info
|
| ︙ | ︙ | |||
133 134 135 136 137 138 139 |
if( g.argc==2 ){
int vid;
/* 012345678901234 */
db_record_repository_filename(0);
printf("project-name: %s\n", db_get("project-name", "<unnamed>"));
printf("repository: %s\n", db_lget("repository", ""));
printf("local-root: %s\n", g.zLocalRoot);
| | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
if( g.argc==2 ){
int vid;
/* 012345678901234 */
db_record_repository_filename(0);
printf("project-name: %s\n", db_get("project-name", "<unnamed>"));
printf("repository: %s\n", db_lget("repository", ""));
printf("local-root: %s\n", g.zLocalRoot);
#if defined(_WIN32)
if( g.zHome ){
printf("user-home: %s\n", g.zHome);
}
#endif
printf("project-code: %s\n", db_get("project-code", ""));
printf("server-code: %s\n", db_get("server-code", ""));
vid = db_lget_int("checkout", 0);
|
| ︙ | ︙ | |||
185 186 187 188 189 190 191 |
cnt++;
if( cnt==1 ){
@ <div class="section">Tags And Properties</div>
@ <ul>
}
@ <li>
if( tagtype==0 ){
| | | | | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
cnt++;
if( cnt==1 ){
@ <div class="section">Tags And Properties</div>
@ <ul>
}
@ <li>
if( tagtype==0 ){
@ <span class="infoTagCancelled">%h(zTagname)</span> cancelled
}else if( zValue ){
@ <span class="infoTag">%h(zTagname)=%h(zValue)</span>
}else {
@ <span class="infoTag">%h(zTagname)</span>
}
if( tagtype==2 ){
if( zOrigUuid && zOrigUuid[0] ){
@ inherited from
hyperlink_to_uuid(zOrigUuid);
}else{
@ propagates to descendants
|
| ︙ | ︙ | |||
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
}else{
@ added by
}
hyperlink_to_uuid(zSrcUuid);
@ on
hyperlink_to_date(zDate,0);
}
}
db_finalize(&q);
if( cnt ){
@ </ul>
}
}
/*
** Append the difference between two RIDs to the output
*/
static void append_diff(int fromid, int toid){
Blob from, to, out;
content_get(fromid, &from);
content_get(toid, &to);
blob_zero(&out);
| > | | 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 |
}else{
@ added by
}
hyperlink_to_uuid(zSrcUuid);
@ on
hyperlink_to_date(zDate,0);
}
@ </li>
}
db_finalize(&q);
if( cnt ){
@ </ul>
}
}
/*
** Append the difference between two RIDs to the output
*/
static void append_diff(int fromid, int toid){
Blob from, to, out;
content_get(fromid, &from);
content_get(toid, &to);
blob_zero(&out);
text_diff(&from, &to, &out, 5, 1);
@ %h(blob_str(&out))
blob_reset(&from);
blob_reset(&to);
blob_reset(&out);
}
/*
|
| ︙ | ︙ | |||
253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
if( !g.okHistory ){
if( zNew==0 ){
@ <p>Deleted %h(zName)</p>
}else if( zOld==0 ){
@ <p>Added %h(zName)</p>
}else{
@ <p>Changes to %h(zName)</p>
}
}else if( zOld && zNew ){
@ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
@ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a>
@ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a>
if( !showDiff ){
@
| > > > > > > > | > | 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 |
if( !g.okHistory ){
if( zNew==0 ){
@ <p>Deleted %h(zName)</p>
}else if( zOld==0 ){
@ <p>Added %h(zName)</p>
}else{
@ <p>Changes to %h(zName)</p>
if( showDiff ){
int rid1 = uuid_to_rid(zOld, 0);
int rid2 = uuid_to_rid(zNew, 0);
@ <blockquote><pre>
append_diff(rid1, rid2);
@ </pre></blockquote>
}
}
}else if( zOld && zNew ){
@ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
@ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a>
@ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a>
if( !showDiff ){
@
@ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a>
}else{
int rid1 = uuid_to_rid(zOld, 0);
int rid2 = uuid_to_rid(zNew, 0);
@ <blockquote><pre>
append_diff(rid1, rid2);
@ </pre></blockquote>
}
@ </p>
}else if( zOld ){
@ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
@ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a></p>
}else{
@ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
@ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a></p>
}
|
| ︙ | ︙ | |||
338 339 340 341 342 343 344 |
zEComment = db_text(0,
"SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
TAG_COMMENT, rid);
zUser = db_column_text(&q, 2);
zComment = db_column_text(&q, 3);
zDate = db_column_text(&q,1);
@ <div class="section">Overview</div>
| | | 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
zEComment = db_text(0,
"SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
TAG_COMMENT, rid);
zUser = db_column_text(&q, 2);
zComment = db_column_text(&q, 3);
zDate = db_column_text(&q,1);
@ <div class="section">Overview</div>
@ <table class="label-value">
@ <tr><th>SHA1 Hash:</th><td>%s(zUuid)
if( g.okSetup ){
@ (Record ID: %d(rid))
}
@ </td></tr>
@ <tr><th>Date:</th><td>
hyperlink_to_date(zDate, "</td></tr>");
|
| ︙ | ︙ | |||
381 382 383 384 385 386 387 |
@ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr>
}
db_finalize(&q);
}
if( g.okHistory ){
const char *zProjName = db_get("project-name", "unnamed");
@ <tr><th>Timelines:</th><td>
| | | | > | | > | | 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 |
@ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr>
}
db_finalize(&q);
}
if( g.okHistory ){
const char *zProjName = db_get("project-name", "unnamed");
@ <tr><th>Timelines:</th><td>
@ <a href="%s(g.zBaseURL)/timeline?p=%S(zUuid)">ancestors</a>
@ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)">descendants</a>
@ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)&p=%S(zUuid)">both</a>
db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag "
" WHERE rid=%d AND tagtype>0 "
" AND tag.tagid=tagxref.tagid "
" AND +tag.tagname GLOB 'sym-*'", rid);
while( db_step(&q)==SQLITE_ROW ){
const char *zTagName = db_column_text(&q, 0);
@ | <a href="%s(g.zTop)/timeline?r=%T(zTagName)">%h(zTagName)</a>
}
db_finalize(&q);
@ </td></tr>
@ <tr><th>Other Links:</th>
@ <td>
@ <a href="%s(g.zTop)/dir?ci=%S(zUuid)">files</a>
if( g.okZip ){
@ | <a href="%s(g.zTop)/zip/%s(zProjName)-%S(zUuid).zip?uuid=%s(zUuid)">
@ ZIP archive</a>
}
@ | <a href="%s(g.zTop)/artifact/%S(zUuid)">manifest</a>
if( g.okWrite ){
@ | <a href="%s(g.zTop)/ci_edit?r=%S(zUuid)">edit</a>
}
@ </td>
@ </tr>
}
@ </table>
}else{
style_header("Check-in Information");
login_anonymous_available();
}
db_finalize(&q);
showTags(rid, "");
@ <div class="section">Changes</div>
|
| ︙ | ︙ | |||
598 599 600 601 602 603 604 | } db_finalize(&q); } /* ** WEBPAGE: vdiff | | | 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 |
}
db_finalize(&q);
}
/*
** WEBPAGE: vdiff
** URL: /vdiff?from=UUID&to=UUID&detail=BOOLEAN
**
** Show all differences between two checkins.
*/
void vdiff_page(void){
int ridFrom, ridTo;
int showDetail = 0;
int iFrom, iTo;
|
| ︙ | ︙ | |||
620 621 622 623 624 625 626 |
if( vdiff_parse_manifest("to", &ridTo, &mTo) ) return;
showDetail = atoi(PD("detail","0"));
style_header("Check-in Differences");
@ <h2>Difference From:</h2><blockquote>
checkin_description(ridFrom);
@ </blockquote><h2>To:</h2><blockquote>
checkin_description(ridTo);
| | | 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 |
if( vdiff_parse_manifest("to", &ridTo, &mTo) ) return;
showDetail = atoi(PD("detail","0"));
style_header("Check-in Differences");
@ <h2>Difference From:</h2><blockquote>
checkin_description(ridFrom);
@ </blockquote><h2>To:</h2><blockquote>
checkin_description(ridTo);
@ </blockquote><hr /><p>
iFrom = iTo = 0;
while( iFrom<mFrom.nFile && iTo<mTo.nFile ){
int cmp;
if( iFrom>=mFrom.nFile ){
cmp = +1;
}else if( iTo>=mTo.nFile ){
|
| ︙ | ︙ | |||
862 863 864 865 866 867 868 |
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
v1 = name_to_rid_www("v1");
v2 = name_to_rid_www("v2");
if( v1==0 || v2==0 ) fossil_redirect_home();
style_header("Diff");
@ <h2>Differences From:</h2>
| | | | | | | | 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 |
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
v1 = name_to_rid_www("v1");
v2 = name_to_rid_www("v2");
if( v1==0 || v2==0 ) fossil_redirect_home();
style_header("Diff");
@ <h2>Differences From:</h2>
@ <blockquote><p>
object_description(v1, 1, 0);
@ </p></blockquote>
@ <h2>To:</h2>
@ <blockquote><p>
object_description(v2, 1, 0);
@ </p></blockquote>
@ <hr />
@ <blockquote><pre>
content_get(v1, &c1);
content_get(v2, &c2);
blob_zero(&diff);
text_diff(&c1, &c2, &diff, 4, 1);
blob_reset(&c1);
blob_reset(&c2);
@ %h(blob_str(&diff))
@ </pre></blockquote>
blob_reset(&diff);
style_footer();
}
|
| ︙ | ︙ | |||
976 977 978 979 980 981 982 |
rid = name_to_rid_www("name");
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
if( rid==0 ) fossil_redirect_home();
if( g.okAdmin ){
const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
| | | | | | 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 |
rid = name_to_rid_www("name");
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
if( rid==0 ) fossil_redirect_home();
if( g.okAdmin ){
const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
g.zTop, zUuid);
}else{
style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
g.zTop, zUuid);
}
}
style_header("Hex Artifact Content");
zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid);
@ <h2>Artifact %s(zUuid):</h2>
@ <blockquote><p>
blob_zero(&downloadName);
object_description(rid, 0, &downloadName);
style_submenu_element("Download", "Download",
"%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
@ </p></blockquote>
@ <hr />
content_get(rid, &content);
@ <blockquote><pre>
hexdump(&content);
@ </pre></blockquote>
style_footer();
}
|
| ︙ | ︙ | |||
1058 1059 1060 1061 1062 1063 1064 |
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
if( rid==0 ) fossil_redirect_home();
if( g.okAdmin ){
const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
| | | | | | | | | 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 |
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
if( rid==0 ) fossil_redirect_home();
if( g.okAdmin ){
const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
g.zTop, zUuid);
}else{
style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
g.zTop, zUuid);
}
}
style_header("Artifact Content");
zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
@ <h2>Artifact %s(zUuid)</h2>
@ <blockquote><p>
blob_zero(&downloadName);
object_description(rid, 0, &downloadName);
style_submenu_element("Download", "Download",
"%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
zMime = mimetype_from_name(blob_str(&downloadName));
if( zMime ){
if( strcmp(zMime, "text/html")==0 ){
if( P("txt") ){
style_submenu_element("Html", "Html",
"%s/artifact?name=%s", g.zTop, zUuid);
}else{
renderAsHtml = 1;
style_submenu_element("Text", "Text",
"%s/artifact?name=%s&txt=1", g.zTop, zUuid);
}
}else if( strcmp(zMime, "application/x-fossil-wiki")==0 ){
if( P("txt") ){
style_submenu_element("Wiki", "Wiki",
"%s/artifact?name=%s", g.zTop, zUuid);
}else{
renderAsWiki = 1;
style_submenu_element("Text", "Text",
"%s/artifact?name=%s&txt=1", g.zTop, zUuid);
}
}
}
@ </p></blockquote>
@ <hr />
content_get(rid, &content);
if( renderAsWiki ){
wiki_convert(&content, 0, 0);
}else if( renderAsHtml ){
@ <div>
cgi_append_content(blob_buffer(&content), blob_size(&content));
@ </div>
}else{
zMime = mimetype_from_content(&content);
@ <blockquote>
if( zMime==0 ){
@ <pre>
@ %h(blob_str(&content))
@ </pre>
style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
}else if( strncmp(zMime, "image/", 6)==0 ){
@ <img src="%s(g.zBaseURL)/raw?name=%s(zUuid)&m=%s(zMime)"></img>
style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
}else{
@ <pre>
hexdump(&content);
@ </pre>
}
@ </blockquote>
|
| ︙ | ︙ | |||
1146 1147 1148 1149 1150 1151 1152 |
login_check_credentials();
if( !g.okRdTkt ){ login_needed(); return; }
rid = name_to_rid_www("name");
if( rid==0 ){ fossil_redirect_home(); }
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
if( g.okAdmin ){
if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
| | | 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 |
login_check_credentials();
if( !g.okRdTkt ){ login_needed(); return; }
rid = name_to_rid_www("name");
if( rid==0 ){ fossil_redirect_home(); }
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
if( g.okAdmin ){
if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
g.zTop, zUuid);
}else{
style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
g.zTop, zUuid);
}
}
content_get(rid, &content);
|
| ︙ | ︙ | |||
1424 1425 1426 1427 1428 1429 1430 |
if( P("preview") ){
Blob suffix;
int nTag = 0;
@ <b>Preview:</b>
@ <blockquote>
@ <table border=0>
if( zNewColor && zNewColor[0] ){
| | | 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 |
if( P("preview") ){
Blob suffix;
int nTag = 0;
@ <b>Preview:</b>
@ <blockquote>
@ <table border=0>
if( zNewColor && zNewColor[0] ){
@ <tr><td style="background-color: %h(zNewColor);">
}else{
@ <tr><td>
}
wiki_convert(&comment, 0, WIKI_INLINE);
blob_zero(&suffix);
blob_appendf(&suffix, "(user: %h", zNewUser);
db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
|
| ︙ | ︙ | |||
1449 1450 1451 1452 1453 1454 1455 |
nTag++;
}
db_finalize(&q);
blob_appendf(&suffix, ")");
@ %s(blob_str(&suffix))
@ </td></tr></table>
@ </blockquote>
| | | 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 |
nTag++;
}
db_finalize(&q);
blob_appendf(&suffix, ")");
@ %s(blob_str(&suffix))
@ </td></tr></table>
@ </blockquote>
@ <hr />
blob_reset(&suffix);
}
@ <p>Make changes to attributes of check-in
@ [<a href="ci?name=%s(zUuid)">%s(zUuid)</a>]:</p>
@ <form action="%s(g.zBaseURL)/ci_edit" method="POST">
login_insert_csrf_secret();
@ <input type="hidden" name="r" value="%S(zUuid)">
|
| ︙ | ︙ | |||
1487 1488 1489 1490 1491 1492 1493 |
}else{
@ <input type="checkbox" name="pclr">
}
@ Propagate color to descendants</input></td></tr>
@ <tr>
for(i=0; i<nColor; i++){
if( aColor[i].zColor[0] ){
| | | 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 |
}else{
@ <input type="checkbox" name="pclr">
}
@ Propagate color to descendants</input></td></tr>
@ <tr>
for(i=0; i<nColor; i++){
if( aColor[i].zColor[0] ){
@ <td style="background-color: %h(aColor[i].zColor);">
}else{
@ <td>
}
if( strcmp(zNewColor, aColor[i].zColor)==0 ){
@ <input type="radio" name="clr" value="%h(aColor[i].zColor)" checked>
}else{
@ <input type="radio" name="clr" value="%h(aColor[i].zColor)">
|
| ︙ | ︙ | |||
1523 1524 1525 1526 1527 1528 1529 |
);
while( db_step(&q)==SQLITE_ROW ){
int tagid = db_column_int(&q, 0);
const char *zTagName = db_column_text(&q, 1);
char zLabel[30];
sprintf(zLabel, "c%d", tagid);
if( P(zLabel) ){
| | | | 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 |
);
while( db_step(&q)==SQLITE_ROW ){
int tagid = db_column_int(&q, 0);
const char *zTagName = db_column_text(&q, 1);
char zLabel[30];
sprintf(zLabel, "c%d", tagid);
if( P(zLabel) ){
@ <br /><input type="checkbox" name="c%d(tagid)" checked>
}else{
@ <br /><input type="checkbox" name="c%d(tagid)">
}
if( strncmp(zTagName, "sym-", 4)==0 ){
@ Cancel tag <b>%h(&zTagName[4])</b>
}else{
@ Cancel special tag <b>%h(zTagName)</b>
}
}
|
| ︙ | ︙ |
Changes to src/login.c.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 | ** not really the point. The anonymous login keeps search-engine ** crawlers and site download tools like wget from walking change ** logs and downloading diffs of very version of the archive that ** has ever existed, and things like that. */ #include "config.h" #include "login.h" | | > | > | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
** not really the point. The anonymous login keeps search-engine
** crawlers and site download tools like wget from walking change
** logs and downloading diffs of very version of the archive that
** has ever existed, and things like that.
*/
#include "config.h"
#include "login.h"
#if defined(_WIN32)
# include <windows.h> /* for Sleep */
# if defined(__MINGW32__) || defined(_MSC_VER)
# define sleep Sleep /* windows does not have sleep, but Sleep */
# endif
#endif
#include <time.h>
/*
** Return the name of the login cookie
*/
static char *login_cookie_name(void){
|
| ︙ | ︙ | |||
149 150 151 152 153 154 155 |
if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin);
if( db_int(1, "SELECT 0 FROM user"
" WHERE uid=%d AND (pw=%Q OR pw=%Q)",
g.userUid, zPasswd, zSha1Pw) ){
sleep(1);
zErrMsg =
| | | | | | 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 |
if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin);
if( db_int(1, "SELECT 0 FROM user"
" WHERE uid=%d AND (pw=%Q OR pw=%Q)",
g.userUid, zPasswd, zSha1Pw) ){
sleep(1);
zErrMsg =
@ <p><span class="loginError">
@ You entered an incorrect old password while attempting to change
@ your password. Your password is unchanged.
@ </span></p>
;
}else if( strcmp(zNew1,zNew2)!=0 ){
zErrMsg =
@ <p><span class="loginError">
@ The two copies of your new passwords do not match.
@ Your password is unchanged.
@ </span></p>
;
}else{
char *zNewPw = sha1_shared_secret(zNew1, g.zLogin);
db_multi_exec(
"UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
);
redirect_to_g();
|
| ︙ | ︙ | |||
202 203 204 205 206 207 208 |
" AND login NOT IN ('anonymous','nobody','developer','reader')"
" AND (pw=%Q OR pw=%Q)",
zUsername, zPasswd, zSha1Pw
);
if( uid<=0 ){
sleep(1);
zErrMsg =
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
" AND login NOT IN ('anonymous','nobody','developer','reader')"
" AND (pw=%Q OR pw=%Q)",
zUsername, zPasswd, zSha1Pw
);
if( uid<=0 ){
sleep(1);
zErrMsg =
@ <p><span class="loginError">
@ You entered an unknown user or an incorrect password.
@ </span></p>
;
}else{
char *zCookie;
const char *zCookieName = login_cookie_name();
const char *zExpire = db_get("cookie-expire","8766");
int expires = atoi(zExpire)*3600;
const char *zIpAddr = PD("REMOTE_ADDR","nil");
zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid);
cgi_set_cookie(zCookieName, zCookie, 0, expires);
db_multi_exec(
"UPDATE user SET cookie=%Q, ipaddr=%Q, "
" cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
zCookie, zIpAddr, expires, uid
);
redirect_to_g();
}
}
style_header("Login/Logout");
@ %s(zErrMsg)
@ <form action="login" method="post">
if( P("g") ){
@ <input type="hidden" name="g" value="%h(P("g"))" />
}
@ <table class="login_out">
@ <tr>
@ <td class="login_out_label">User ID:</td>
if( anonFlag ){
@ <td><input type="text" id="u" name="u" value="anonymous" size="30" /></td>
}else{
@ <td><input type="text" id="u" name="u" value="" size="30" /></td>
}
@ </tr>
@ <tr>
@ <td class="login_out_label">Password:</td>
@ <td><input type="password" id="p" name="p" value="" size="30" /></td>
@ </tr>
if( g.zLogin==0 ){
zAnonPw = db_text(0, "SELECT pw FROM user"
" WHERE login='anonymous'"
" AND cap!=''");
}
@ <tr>
@ <td></td>
@ <td><input type="submit" name="in" value="Login" /></td>
@ </tr>
@ </table>
@ <script type="text/JavaScript">document.getElementById('u').focus()</script>
if( g.zLogin==0 ){
@ <p>Enter
}else{
@ <p>You are currently logged in as <b>%h(g.zLogin)</b></p>
@ <p>To change your login to a different user, enter
}
@ your user-id and password at the left and press the
@ "Login" button. Your user name will be stored in a browser cookie.
@ You must configure your web browser to accept cookies in order for
@ the login to take.</p>
if( zAnonPw ){
unsigned int uSeed = captcha_seed();
char const *zDecoded = captcha_decode(uSeed);
int bAutoCaptcha = db_get_boolean("auto-captcha", 1);
char *zCaptcha = captcha_render(zDecoded);
@ <p><input type="hidden" name="cs" value="%u(uSeed)" />
@ Visitors may enter <b>anonymous</b> as the user-ID with
@ the 8-character hexadecimal password shown below:</p>
@ <div class="captcha"><table class="captcha"><tr><td><pre>
@ %s(zCaptcha)
@ </pre></td></tr></table>
if( bAutoCaptcha ) {
@ <input type="button" value="Fill out captcha"
@ onclick="document.getElementById('u').value='anonymous';
@ document.getElementById('p').value='%s(zDecoded)';" />
}
@ </div>
free(zCaptcha);
}
if( g.zLogin ){
@ <hr />
@ <p>To log off the system (and delete your login cookie)
@ press the following button:<br />
@ <input type="submit" name="out" value="Logout" /></p>
}
@ </form>
if( g.okPassword ){
@ <hr />
@ <p>To change your password, enter your old password and your
@ new password twice below then press the "Change Password"
@ button.</p>
@ <form action="login" method="post">
@ <table>
@ <tr><td class="login_out_label">Old Password:</td>
@ <td><input type="password" name="p" size="30" /></td></tr>
@ <tr><td class="login_out_label">New Password:</td>
@ <td><input type="password" name="n1" size="30" /></td></tr>
@ <tr><td class="login_out_label">Repeat New Password:</td>
@ <td><input type="password" name="n2" size="30" /></td></tr>
@ <tr><td></td>
@ <td><input type="submit" value="Change Password" /></td></tr>
@ </table>
@ </form>
}
style_footer();
}
|
| ︙ | ︙ | |||
592 593 594 595 596 597 598 |
*/
void login_anonymous_available(void){
if( !g.okHistory &&
db_exists("SELECT 1 FROM user"
" WHERE login='anonymous'"
" AND cap LIKE '%%h%%'") ){
const char *zUrl = PD("REQUEST_URI", "index");
| | | | | 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 |
*/
void login_anonymous_available(void){
if( !g.okHistory &&
db_exists("SELECT 1 FROM user"
" WHERE login='anonymous'"
" AND cap LIKE '%%h%%'") ){
const char *zUrl = PD("REQUEST_URI", "index");
@ <p>Many <span class="disabled">hyperlinks are disabled.</span><br />
@ Use <a href="%s(g.zTop)/login?anon=1&g=%T(zUrl)">anonymous login</a>
@ to enable hyperlinks.</p>
}
}
/*
** While rendering a form, call this routine to add the Anti-CSRF token
** as a hidden element of the form.
*/
void login_insert_csrf_secret(void){
@ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)" />
}
/*
** Before using the results of a form, first call this routine to verify
** that ths Anti-CSRF token is present and is valid. If the Anti-CSRF token
** is missing or is incorrect, that indicates a cross-site scripting attach
** so emits an error message and abort.
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | int xlinkClusterOnly; /* Set when cloning. Only process clusters */ int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ int *aCommitFile; /* Array of files to be committed */ int markPrivate; /* All new artifacts are private if true */ int urlIsFile; /* True if a "file:" url */ int urlIsHttps; /* True if a "https:" url */ char *urlName; /* Hostname for http: or filename for file: */ char *urlHostname; /* The HOST: parameter on http headers */ char *urlProtocol; /* "http" or "https" */ int urlPort; /* TCP port number for http: or https: */ int urlDfltPort; /* The default port for the given protocol */ char *urlPath; /* Pathname for http: */ char *urlUser; /* User id for http: */ char *urlPasswd; /* Password for http: */ char *urlCanonical; /* Canonical representation of the URL */ char *urlProxyAuth; /* Proxy-Authorizer: string */ int dontKeepUrl; /* Do not persist the URL */ const char *zLogin; /* Login name. "" if not logged in. */ int noPswd; /* Logged in without password (on 127.0.0.1) */ int userUid; /* Integer user id */ /* Information used to populate the RCVFROM table */ | > > | 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 | int xlinkClusterOnly; /* Set when cloning. Only process clusters */ int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ int *aCommitFile; /* Array of files to be committed */ int markPrivate; /* All new artifacts are private if true */ int urlIsFile; /* True if a "file:" url */ int urlIsHttps; /* True if a "https:" url */ int urlIsSsh; /* True if an "ssh:" url */ char *urlName; /* Hostname for http: or filename for file: */ char *urlHostname; /* The HOST: parameter on http headers */ char *urlProtocol; /* "http" or "https" */ int urlPort; /* TCP port number for http: or https: */ int urlDfltPort; /* The default port for the given protocol */ char *urlPath; /* Pathname for http: */ char *urlUser; /* User id for http: */ char *urlPasswd; /* Password for http: */ char *urlCanonical; /* Canonical representation of the URL */ char *urlProxyAuth; /* Proxy-Authorizer: string */ char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ int dontKeepUrl; /* Do not persist the URL */ const char *zLogin; /* Login name. "" if not logged in. */ int noPswd; /* Logged in without password (on 127.0.0.1) */ int userUid; /* Integer user id */ /* Information used to populate the RCVFROM table */ |
| ︙ | ︙ | |||
628 629 630 631 632 633 634 |
** zRepo might be a directory itself. In that case chroot into
** the directory zRepo.
**
** Assume the user-id and group-id of the repository, or if zRepo
** is a directory, of that directory.
*/
static char *enter_chroot_jail(char *zRepo){
| | | 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 |
** zRepo might be a directory itself. In that case chroot into
** the directory zRepo.
**
** Assume the user-id and group-id of the repository, or if zRepo
** is a directory, of that directory.
*/
static char *enter_chroot_jail(char *zRepo){
#if !defined(_WIN32)
if( getuid()==0 ){
int i;
struct stat sStat;
Blob dir;
char *zDir;
file_canonical_name(zRepo, &dir);
|
| ︙ | ︙ | |||
806 807 808 809 810 811 812 |
if( g.argc==3 && strcmp(g.argv[1],"cgi")==0 ){
zFile = g.argv[2];
}else{
zFile = g.argv[1];
}
g.httpOut = stdout;
g.httpIn = stdin;
| | | 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 |
if( g.argc==3 && strcmp(g.argv[1],"cgi")==0 ){
zFile = g.argv[2];
}else{
zFile = g.argv[1];
}
g.httpOut = stdout;
g.httpIn = stdin;
#if defined(_WIN32)
/* Set binary mode on windows to avoid undesired translations
** between \n and \r\n. */
setmode(_fileno(g.httpOut), _O_BINARY);
setmode(_fileno(g.httpIn), _O_BINARY);
#endif
#ifdef __EMX__
/* Similar hack for OS/2 */
|
| ︙ | ︙ | |||
912 913 914 915 916 917 918 919 |
** not select a valid repository and the --notfound option is available,
** then the server redirects (HTTP code 302) to the URL of --notfound.
*/
void cmd_http(void){
const char *zIpAddr;
const char *zNotFound;
zNotFound = find_option("notfound", 0, 1);
if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){
| > | < > > | > > > > > > | | 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 |
** not select a valid repository and the --notfound option is available,
** then the server redirects (HTTP code 302) to the URL of --notfound.
*/
void cmd_http(void){
const char *zIpAddr;
const char *zNotFound;
zNotFound = find_option("notfound", 0, 1);
g.cgiOutput = 1;
if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){
fossil_fatal("no repository specified");
}
g.fullHttpReply = 1;
if( g.argc==6 ){
g.httpIn = fopen(g.argv[3], "rb");
g.httpOut = fopen(g.argv[4], "wb");
zIpAddr = g.argv[5];
}else{
g.httpIn = stdin;
g.httpOut = stdout;
zIpAddr = 0;
}
find_server_repository(0);
g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
cgi_handle_http_request(zIpAddr);
process_one_web_page(zNotFound);
}
/*
** 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.
*/
void cmd_test_http(void){
login_set_capabilities("s");
g.httpIn = stdin;
g.httpOut = stdout;
find_server_repository(0);
g.cgiOutput = 1;
g.fullHttpReply = 1;
cgi_handle_http_request(0);
process_one_web_page(0);
}
#if !defined(_WIN32)
#if !defined(__DARWIN__) && !defined(__APPLE__)
/*
** Search for an executable on the PATH environment variable.
** Return true (1) if found and false (0) if not found.
*/
static int binaryOnPath(const char *zBinary){
const char *zPath = getenv("PATH");
|
| ︙ | ︙ | |||
980 981 982 983 984 985 986 | ** Open a socket and begin listening and responding to HTTP requests on ** TCP port 8080, or on any other TCP port defined by the -P or ** --port option. The optional argument is the name of the repository. ** The repository argument may be omitted if the working directory is ** within an open checkout. ** ** The "ui" command automatically starts a web browser after initializing | | > > | > | | 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 |
** Open a socket and begin listening and responding to HTTP requests on
** TCP port 8080, or on any other TCP port defined by the -P or
** --port option. The optional argument is the name of the repository.
** The repository argument may be omitted if the working directory is
** within an open checkout.
**
** The "ui" command automatically starts a web browser after initializing
** the web server. The "ui" command also binds to 127.0.0.1 and so will
** only process HTTP traffic from the local machine.
**
** In the "server" command, the REPOSITORY can be a directory (aka folder)
** that contains one or more respositories with names ending in ".fossil".
** In that case, the first element of the URL is used to select among the
** various repositories.
*/
void cmd_webserver(void){
int iPort, mxPort; /* Range of TCP ports allowed */
const char *zPort; /* Value of the --port option */
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' */
const char *zNotFound; /* The --notfound option or NULL */
int flags = 0; /* Server flags */
#if defined(_WIN32)
const char *zStopperFile; /* Name of file used to terminate server */
zStopperFile = find_option("stopper", 0, 1);
#endif
g.thTrace = find_option("th-trace", 0, 0)!=0;
if( g.thTrace ){
blob_zero(&g.thLog);
}
zPort = find_option("port", "P", 1);
zNotFound = find_option("notfound", 0, 1);
if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
isUiCmd = g.argv[1][0]=='u';
if( isUiCmd ) flags |= HTTP_SERVER_LOCALHOST;
find_server_repository(isUiCmd);
if( zPort ){
iPort = mxPort = atoi(zPort);
}else{
iPort = db_get_int("http-port", 8080);
mxPort = iPort+100;
}
#if !defined(_WIN32)
/* Unix implementation */
if( isUiCmd ){
#if !defined(__DARWIN__) && !defined(__APPLE__)
zBrowser = db_get("web-browser", 0);
if( zBrowser==0 ){
static char *azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" };
int i;
|
| ︙ | ︙ | |||
1037 1038 1039 1040 1041 1042 1043 |
}
#else
zBrowser = db_get("web-browser", "open");
#endif
zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser);
}
db_close();
| | | | 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 |
}
#else
zBrowser = db_get("web-browser", "open");
#endif
zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser);
}
db_close();
if( cgi_http_server(iPort, mxPort, zBrowserCmd, flags) ){
fossil_fatal("unable to listen on TCP socket %d", iPort);
}
g.httpIn = stdin;
g.httpOut = stdout;
if( g.fHttpTrace || g.fSqlTrace ){
fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
}
g.cgiOutput = 1;
find_server_repository(isUiCmd);
g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
cgi_handle_http_request(0);
process_one_web_page(zNotFound);
#else
/* Win32 implementation */
if( isUiCmd ){
zBrowser = db_get("web-browser", "start");
zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser);
}
db_close();
win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound, flags);
#endif
}
|
Changes to src/main.mk.
| ︙ | ︙ | |||
49 50 51 52 53 54 55 56 57 58 59 60 61 62 | $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/name.c \ $(SRCDIR)/pivot.c \ $(SRCDIR)/pqueue.c \ $(SRCDIR)/printf.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ | > | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/name.c \ $(SRCDIR)/pivot.c \ $(SRCDIR)/popen.c \ $(SRCDIR)/pqueue.c \ $(SRCDIR)/printf.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ |
| ︙ | ︙ | |||
121 122 123 124 125 126 127 128 129 130 131 132 133 134 | main_.c \ manifest_.c \ md5_.c \ merge_.c \ merge3_.c \ name_.c \ pivot_.c \ pqueue_.c \ printf_.c \ rebuild_.c \ report_.c \ rss_.c \ schema_.c \ search_.c \ | > | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | main_.c \ manifest_.c \ md5_.c \ merge_.c \ merge3_.c \ name_.c \ pivot_.c \ popen_.c \ pqueue_.c \ printf_.c \ rebuild_.c \ report_.c \ rss_.c \ schema_.c \ search_.c \ |
| ︙ | ︙ | |||
193 194 195 196 197 198 199 200 201 202 203 204 205 206 | $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/name.o \ $(OBJDIR)/pivot.o \ $(OBJDIR)/pqueue.o \ $(OBJDIR)/printf.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ | > | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/name.o \ $(OBJDIR)/pivot.o \ $(OBJDIR)/popen.o \ $(OBJDIR)/pqueue.o \ $(OBJDIR)/printf.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ |
| ︙ | ︙ | |||
268 269 270 271 272 273 274 | # $(SRCDIR)/../manifest: # noop clean: rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h rm -f translate makeheaders mkindex page_index.h headers | | | | 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | # $(SRCDIR)/../manifest: # noop clean: rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h rm -f translate makeheaders mkindex page_index.h headers rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h popen.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h page_index.h: $(TRANS_SRC) mkindex ./mkindex $(TRANS_SRC) >$@ headers: page_index.h makeheaders VERSION.h ./makeheaders add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h touch headers headers: Makefile Makefile: add_.c: $(SRCDIR)/add.c translate ./translate $(SRCDIR)/add.c >add_.c $(OBJDIR)/add.o: add_.c add.h $(SRCDIR)/config.h |
| ︙ | ︙ | |||
557 558 559 560 561 562 563 564 565 566 567 568 569 570 | pivot_.c: $(SRCDIR)/pivot.c translate ./translate $(SRCDIR)/pivot.c >pivot_.c $(OBJDIR)/pivot.o: pivot_.c pivot.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pivot.o -c pivot_.c pivot.h: headers pqueue_.c: $(SRCDIR)/pqueue.c translate ./translate $(SRCDIR)/pqueue.c >pqueue_.c $(OBJDIR)/pqueue.o: pqueue_.c pqueue.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pqueue.o -c pqueue_.c pqueue.h: headers | > > > > > > > | 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 | pivot_.c: $(SRCDIR)/pivot.c translate ./translate $(SRCDIR)/pivot.c >pivot_.c $(OBJDIR)/pivot.o: pivot_.c pivot.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pivot.o -c pivot_.c pivot.h: headers popen_.c: $(SRCDIR)/popen.c translate ./translate $(SRCDIR)/popen.c >popen_.c $(OBJDIR)/popen.o: popen_.c popen.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/popen.o -c popen_.c popen.h: headers pqueue_.c: $(SRCDIR)/pqueue.c translate ./translate $(SRCDIR)/pqueue.c >pqueue_.c $(OBJDIR)/pqueue.o: pqueue_.c pqueue.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pqueue.o -c pqueue_.c pqueue.h: headers |
| ︙ | ︙ |
Changes to src/makeheaders.c.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 | */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <memory.h> #include <sys/stat.h> #include <assert.h> | > | > > | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <memory.h> #include <sys/stat.h> #include <assert.h> #if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) # ifndef WIN32 # define WIN32 # endif # include <string.h> #else # include <unistd.h> #endif /* ** Macros for debugging. */ #ifdef DEBUG static int debugMask = 0; |
| ︙ | ︙ | |||
2139 2140 2141 2142 2143 2144 2145 |
** and EXPORT_INTERFACE and LOCAL_INTERFACE
*/
zArg = &zCmd[2];
while( *zArg && isspace(*zArg) && *zArg!='\n' ){
zArg++;
}
if( *zArg==0 || *zArg=='\n' ){ return 0; }
| | | | | 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 |
** and EXPORT_INTERFACE and LOCAL_INTERFACE
*/
zArg = &zCmd[2];
while( *zArg && isspace(*zArg) && *zArg!='\n' ){
zArg++;
}
if( *zArg==0 || *zArg=='\n' ){ return 0; }
nArg = pToken->nText + (int)(pToken->zText - zArg);
if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
}else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Export);
}else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Local);
}else{
PushIfMacro(0,zArg,nArg,pToken->nLine,0);
}
}else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){
/*
** Push an #ifdef.
*/
zArg = &zCmd[5];
while( *zArg && isspace(*zArg) && *zArg!='\n' ){
zArg++;
}
if( *zArg==0 || *zArg=='\n' ){ return 0; }
nArg = pToken->nText + (int)(pToken->zText - zArg);
PushIfMacro("defined",zArg,nArg,pToken->nLine,0);
}else if( nCmd==6 && strncmp(zCmd,"ifndef",6)==0 ){
/*
** Push an #ifndef.
*/
zArg = &zCmd[6];
while( *zArg && isspace(*zArg) && *zArg!='\n' ){
zArg++;
}
if( *zArg==0 || *zArg=='\n' ){ return 0; }
nArg = pToken->nText + (int)(pToken->zText - zArg);
PushIfMacro("!defined",zArg,nArg,pToken->nLine,0);
}else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){
/*
** Invert the #if on the top of the stack
*/
if( ifStack==0 ){
fprintf(stderr,"%s:%d: '#else' without an '#if'\n",zFilename,
|
| ︙ | ︙ | |||
2795 2796 2797 2798 2799 2800 2801 |
nErr++;
}
}else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){
if( report ) fprintf(report,"error!\n");
fprintf(stderr,
"%s: Can't overwrite this file because it wasn't previously\n"
"%*s generated by 'makeheaders'.\n",
| | | 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 |
nErr++;
}
}else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){
if( report ) fprintf(report,"error!\n");
fprintf(stderr,
"%s: Can't overwrite this file because it wasn't previously\n"
"%*s generated by 'makeheaders'.\n",
pFile->zHdr, (int)strlen(pFile->zHdr), "");
nErr++;
}else if( strcmp(zOldVersion,zNewVersion)!=0 ){
if( report ) fprintf(report,"updated\n");
if( WriteFile(pFile->zHdr,zNewVersion) ){
fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr);
nErr++;
}
|
| ︙ | ︙ | |||
2950 2951 2952 2953 2954 2955 2956 |
}
}
if( nLabel==0 ) continue;
zLabel[nLabel] = 0;
InsertExtraDecl(pDecl);
zDecl = pDecl->zDecl;
if( zDecl==0 ) zDecl = pDecl->zFwd;
| | | | | | 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 |
}
}
if( nLabel==0 ) continue;
zLabel[nLabel] = 0;
InsertExtraDecl(pDecl);
zDecl = pDecl->zDecl;
if( zDecl==0 ) zDecl = pDecl->zFwd;
printf("%s %s %s %p %d %d %d %d %d\n",
pDecl->zName,
zLabel,
pDecl->zFile,
pDecl->pComment,
pDecl->pComment ? pDecl->pComment->nText+1 : 0,
pDecl->zIf ? (int)strlen(pDecl->zIf)+1 : 0,
zDecl ? (int)strlen(zDecl) : 0,
pDecl->pComment ? pDecl->pComment->nLine : 0,
pDecl->tokenCode.nText ? pDecl->tokenCode.nText+1 : 0
);
if( pDecl->pComment ){
printf("%.*s\n",pDecl->pComment->nText, pDecl->pComment->zText);
}
if( pDecl->zIf ){
|
| ︙ | ︙ |
Changes to src/makemake.tcl.
| ︙ | ︙ | |||
42 43 44 45 46 47 48 49 50 51 52 53 54 55 | main manifest md5 merge merge3 name pivot pqueue printf rebuild report rss schema search | > | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | main manifest md5 merge merge3 name pivot popen pqueue printf rebuild report rss schema search |
| ︙ | ︙ | |||
78 79 80 81 82 83 84 | zip http_ssl } # Name of the final application # set name fossil | | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
zip
http_ssl
}
# Name of the final application
#
set name fossil
if { 0 == $argc } {
puts {# DO NOT EDIT
#
# This file is automatically generated. Instead of editing this
# file, edit "makemake.tcl" then run "tclsh makemake.tcl >main.mk"
# to regenerate this file.
#
# This file is included by linux-gcc.mk or linux-mingw.mk or possible
|
| ︙ | ︙ | |||
202 203 204 205 206 207 208 | puts "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" puts "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c" puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n" puts "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c" puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n" | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
puts "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n"
puts "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
puts "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n"
exit
}
if { "dmc" == [lindex $argv 0] } {
puts {# DO NOT EDIT
#
# This file is automatically generated. Instead of editing this
# file, edit "makemake.tcl" then run
# "tclsh src/makemake.tcl dmc > win/Makefile.dmc"
# to regenerate this file.
B = ..
SRCDIR = $B\src
OBJDIR = .
O = .obj
E = .exe
# Maybe DMDIR, SSL or INCL needs adjustment
DMDIR = c:\DM
INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(DMDIR)\extra\include
#SSL = -DFOSSIL_ENABLE_SSL=1
SSL =
DMCDEF = -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp
I18N = -DFOSSIL_I18N=0
CFLAGS = -o
BCC = $(DMDIR)\bin\dmc $(CFLAGS)
TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(I18N) $(SSL) $(INCL)
LIBS = $(DMDIR)\extra\lib\ zlib wsock32
}
puts -nonewline "SRC = "
foreach s [lsort $src] {
puts -nonewline "${s}_.c "
}
puts "\n"
puts -nonewline "OBJ = "
foreach s [lsort $src] {
puts -nonewline "\$(OBJDIR)\\$s\$O "
}
puts "\$(OBJDIR)\\sqlite3\$O \$(OBJDIR)\\th\$O \$(OBJDIR)\\th_lang\$O "
puts {
APPNAME = $(OBJDIR)\fossil$(E)
all: $(APPNAME)
$(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\link
cd $(OBJDIR)
$(DMDIR)\bin\link @link
$(OBJDIR)\link: $B\win\Makefile.dmc}
puts -nonewline "\t+echo "
foreach s [lsort $src] {
puts -nonewline "$s "
}
puts "sqlite3 th th_lang > \$@"
puts "\t+echo fossil >> \$@"
puts "\t+echo fossil >> \$@"
puts "\t+echo \$(LIBS) >> \$@\n\n"
puts {
translate$E: $(SRCDIR)\translate.c
$(BCC) -o$@ $**
makeheaders$E: $(SRCDIR)\makeheaders.c
$(BCC) -o$@ $**
mkindex$E: $(SRCDIR)\mkindex.c
$(BCC) -o$@ $**
version$E: $B\win\version.c
$(BCC) -o$@ $**
$(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
$(TCC) -o$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $**
$(OBJDIR)\th$O : $(SRCDIR)\th.c
$(TCC) -o$@ -c $**
$(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
$(TCC) -o$@ -c $**
VERSION.h : version$E $B\manifest.uuid $B\manifest
+$** > $@
page_index.h: mkindex$E $(SRC)
+$** > $@
clean:
-del $(OBJDIR)\*.obj
-del *.obj *_.c *.h *.map
realclean:
-del $(APPNAME) translate$E mkindex$E makeheaders$E version$E
}
foreach s [lsort $src] {
puts "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h"
puts "\t\$(TCC) -o\$@ -c ${s}_.c\n"
puts "${s}_.c : \$(SRCDIR)\\$s.c"
puts "\t+translate\$E \$** > \$@\n"
}
puts -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\t +makeheaders\$E "
foreach s [lsort $src] {
puts -nonewline "${s}_.c:$s.h "
}
puts "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h"
puts "\t@copy /Y nul: headers"
exit
}
if { "msc" == [lindex $argv 0] } {
puts {# DO NOT EDIT
#
# This file is automatically generated. Instead of editing this
# file, edit "makemake.tcl" then run
# "tclsh src/makemake.tcl msc > win/Makefile.msc"
# to regenerate this file.
B = ..
SRCDIR = $B\src
OBJDIR = .
O = .obj
E = .exe
# Maybe MSCDIR, SSL, ZLIB, or INCL needs adjustment
MSCDIR = c:\msc
# Uncomment below for SSL support
SSL =
SSLLIB =
#SSL = -DFOSSIL_ENABLE_SSL=1
#SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib advapi32.lib
# zlib options
# When using precompiled from http://zlib.net/zlib125-dll.zip
#ZINCDIR = C:\zlib125-dll\include
#ZLIBDIR = C:\zlib125-dll\lib
#ZLIB = zdll.lib
ZINCDIR = $(MSCDIR)\extra\include
ZLIBDIR = $(MSCDIR)\extra\lib
ZLIB = zlib.lib
INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR)
MSCDEF = -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp
I18N = -DFOSSIL_I18N=0
CFLAGS = -nologo -MT -O2
BCC = $(CC) $(CFLAGS)
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(I18N) $(SSL) $(INCL)
LIBS = $(ZLIB) ws2_32.lib $(SSLLIB)
LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
}
puts -nonewline "SRC = "
foreach s [lsort $src] {
puts -nonewline "${s}_.c "
}
puts "\n"
puts -nonewline "OBJ = "
foreach s [lsort $src] {
puts -nonewline "\$(OBJDIR)\\$s\$O "
}
puts "\$(OBJDIR)\\sqlite3\$O \$(OBJDIR)\\th\$O \$(OBJDIR)\\th_lang\$O "
puts {
APPNAME = $(OBJDIR)\fossil$(E)
all: $(OBJDIR) $(APPNAME)
$(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\linkopts
cd $(OBJDIR)
link -LINK -OUT:$@ $(LIBDIR) @linkopts
$(OBJDIR)\linkopts: $B\win\Makefile.msc}
puts -nonewline "\techo "
foreach s [lsort $src] {
puts -nonewline "$s "
}
puts "sqlite3 th th_lang > \$@"
puts "\techo \$(LIBS) >> \$@\n\n"
puts {
$(OBJDIR):
@-mkdir $@
translate$E: $(SRCDIR)\translate.c
$(BCC) $**
makeheaders$E: $(SRCDIR)\makeheaders.c
$(BCC) $**
mkindex$E: $(SRCDIR)\mkindex.c
$(BCC) $**
version$E: $B\win\version.c
$(BCC) $**
$(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
$(TCC) /Fo$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $**
$(OBJDIR)\th$O : $(SRCDIR)\th.c
$(TCC) /Fo$@ -c $**
$(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
$(TCC) /Fo$@ -c $**
VERSION.h : version$E $B\manifest.uuid $B\manifest
$** > $@
page_index.h: mkindex$E $(SRC)
$** > $@
clean:
-del $(OBJDIR)\*.obj
-del *.obj *_.c *.h *.map
-del headers linkopts
realclean:
-del $(APPNAME) translate$E mkindex$E makeheaders$E version$E
}
foreach s [lsort $src] {
puts "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h"
puts "\t\$(TCC) /Fo\$@ -c ${s}_.c\n"
puts "${s}_.c : \$(SRCDIR)\\$s.c"
puts "\ttranslate\$E \$** > \$@\n"
}
puts -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\tmakeheaders\$E "
foreach s [lsort $src] {
puts -nonewline "${s}_.c:$s.h "
}
puts "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h"
puts "\t@copy /Y nul: headers"
}
|
Changes to src/merge3.c.
| ︙ | ︙ | |||
163 164 165 166 167 168 169 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is ** an array of integer triples. Within each triple, the first integer ** is the number of lines of text to copy directly from the pivot, ** the second integer is the number of lines of text to omit from the ** pivot, and the third integer is the number of lines of text that are ** inserted. The edit array ends with a triple of 0,0,0. */ | | | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is
** an array of integer triples. Within each triple, the first integer
** is the number of lines of text to copy directly from the pivot,
** the second integer is the number of lines of text to omit from the
** pivot, and the third integer is the number of lines of text that are
** inserted. The edit array ends with a triple of 0,0,0.
*/
aC1 = text_diff(pPivot, pV1, 0, 0, 1);
aC2 = text_diff(pPivot, pV2, 0, 0, 1);
if( aC1==0 || aC2==0 ){
free(aC1);
free(aC2);
return -1;
}
blob_rewind(pV1); /* Rewind inputs: Needed to reconstruct output */
|
| ︙ | ︙ |
Added src/popen.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
/*
** Copyright (c) 2010 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains an implementation of a bi-directional popen().
*/
#include "config.h"
#include "popen.h"
#ifdef _WIN32
#include <windows.h>
#include <fcntl.h>
/*
** Print a fatal error and quit.
*/
static void win32_fatal_error(const char *zMsg){
fossil_fatal("%s");
}
#endif
#ifdef _WIN32
/*
** On windows, create a child process and specify the stdin, stdout,
** and stderr channels for that process to use.
**
** Return the number of errors.
*/
static int win32_create_child_process(
char *zCmd, /* The command that the child process will run */
HANDLE hIn, /* Standard input */
HANDLE hOut, /* Standard output */
HANDLE hErr, /* Standard error */
DWORD *pChildPid /* OUT: Child process handle */
){
STARTUPINFO si;
PROCESS_INFORMATION pi;
BOOL rc;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
SetHandleInformation(hIn, HANDLE_FLAG_INHERIT, TRUE);
si.hStdInput = hIn;
SetHandleInformation(hOut, HANDLE_FLAG_INHERIT, TRUE);
si.hStdOutput = hOut;
SetHandleInformation(hErr, HANDLE_FLAG_INHERIT, TRUE);
si.hStdError = hErr;
rc = CreateProcess(
NULL, /* Application Name */
zCmd, /* Command-line */
NULL, /* Process attributes */
NULL, /* Thread attributes */
TRUE, /* Inherit Handles */
0, /* Create flags */
NULL, /* Environment */
NULL, /* Current directory */
&si, /* Startup Info */
&pi /* Process Info */
);
if( rc ){
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
*pChildPid = pi.dwProcessId;
}else{
win32_fatal_error("cannot create child process");
}
return rc!=0;
}
#endif
/*
** Create a child process running shell command "zCmd". *ppOut is
** a FILE that becomes the standard input of the child process.
** (The caller writes to *ppOut in order to send text to the child.)
** *ppIn is stdout from the child process. (The caller
** reads from *ppIn in order to receive input from the child.)
** Note that *ppIn is an unbuffered file descriptor, not a FILE.
** The process ID of the child is written into *pChildPid.
**
** Return the number of errors.
*/
int popen2(const char *zCmd, int *pfdIn, FILE **ppOut, int *pChildPid){
#ifdef _WIN32
HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr, hStderr;
SECURITY_ATTRIBUTES saAttr;
DWORD childPid = 0;
int fd;
saAttr.nLength = sizeof(saAttr);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
hStderr = GetStdHandle(STD_ERROR_HANDLE);
if( !CreatePipe(&hStdoutRd, &hStdoutWr, &saAttr, 4096) ){
win32_fatal_error("cannot create pipe for stdout");
}
SetHandleInformation( hStdoutRd, HANDLE_FLAG_INHERIT, FALSE);
if( !CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 4096) ){
win32_fatal_error("cannot create pipe for stdin");
}
SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE);
win32_create_child_process((char*)zCmd,
hStdinRd, hStdoutWr, hStderr,&childPid);
*pChildPid = childPid;
*pfdIn = _open_osfhandle((long)hStdoutRd, 0);
fd = _open_osfhandle((long)hStdinWr, 0);
*ppOut = _fdopen(fd, "w");
CloseHandle(hStdinRd);
CloseHandle(hStdoutWr);
return 0;
#else
int pin[2], pout[2];
*pfdIn = 0;
*ppOut = 0;
*pChildPid = 0;
if( pipe(pin)<0 ){
return 1;
}
if( pipe(pout)<0 ){
close(pin[0]);
close(pin[1]);
return 1;
}
*pChildPid = fork();
if( *pChildPid<0 ){
close(pin[0]);
close(pin[1]);
close(pout[0]);
close(pout[1]);
*pChildPid = 0;
return 1;
}
if( *pChildPid==0 ){
/* This is the child process */
close(0);
dup(pout[0]);
close(pout[0]);
close(pout[1]);
close(1);
dup(pin[1]);
close(pin[0]);
close(pin[1]);
execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0);
return 1;
}else{
/* This is the parent process */
close(pin[1]);
*pfdIn = pin[0];
close(pout[0]);
*ppOut = fdopen(pout[1], "w");
return 0;
}
#endif
}
/*
** Close the connection to a child process previously created using
** popen2(). Kill off the child process, then close the pipes.
*/
void pclose2(int fdIn, FILE *pOut, int childPid){
#ifdef _WIN32
/* Not implemented, yet */
close(fdIn);
fclose(pOut);
#else
close(fdIn);
fclose(pOut);
kill(childPid, SIGINT);
#endif
}
|
Changes to src/rebuild.c.
| ︙ | ︙ | |||
444 445 446 447 448 449 450 451 452 453 454 455 456 457 |
continue;
}
blob_appendf(&path, "%s/%s", g.argv[3], pEntry->d_name);
if( blob_read_from_file(&aContent, blob_str(&path))==-1 ){
fossil_panic("Some unknown error occurred while reading \"%s\"", blob_str(&path));
}
content_put(&aContent, 0, 0);
}
}
else {
fossil_panic("Encountered error %d while trying to open \"%s\".", errno, g.argv[3]);
}
rebuild_db(0, 1);
| > > | 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
continue;
}
blob_appendf(&path, "%s/%s", g.argv[3], pEntry->d_name);
if( blob_read_from_file(&aContent, blob_str(&path))==-1 ){
fossil_panic("Some unknown error occurred while reading \"%s\"", blob_str(&path));
}
content_put(&aContent, 0, 0);
blob_reset(&path);
blob_reset(&aContent);
}
}
else {
fossil_panic("Encountered error %d while trying to open \"%s\".", errno, g.argv[3]);
}
rebuild_db(0, 1);
|
| ︙ | ︙ |
Changes to src/report.c.
| ︙ | ︙ | |||
275 276 277 278 279 280 281 | } zTitle = db_column_text(&q, 0); zSQL = db_column_text(&q, 1); zOwner = db_column_text(&q, 2); zClrKey = db_column_text(&q, 3); @ <table cellpadding=0 cellspacing=0 border=0> @ <tr><td valign="top" align="right">Title:</td><td width=15></td> | | | | 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | } zTitle = db_column_text(&q, 0); zSQL = db_column_text(&q, 1); zOwner = db_column_text(&q, 2); zClrKey = db_column_text(&q, 3); @ <table cellpadding=0 cellspacing=0 border=0> @ <tr><td valign="top" align="right">Title:</td><td width=15></td> @ <td colspan="3">%h(zTitle)</td></tr> @ <tr><td valign="top" align="right">Owner:</td><td></td> @ <td colspan="3">%h(zOwner)</td></tr> @ <tr><td valign="top" align="right">SQL:</td><td></td> @ <td valign="top"><pre> @ %h(zSQL) @ </pre></td> @ <td width=15></td><td valign="top"> output_color_key(zClrKey, 0, "border=0 cellspacing=0 cellpadding=3"); @ </td> |
| ︙ | ︙ | |||
391 392 393 394 395 396 397 |
zTitle = mprintf("Copy Of %s", zTitle);
zOwner = g.zLogin;
}
}
if( zOwner==0 ) zOwner = g.zLogin;
style_submenu_element("Cancel", "Cancel", "reportlist");
if( rn>0 ){
| | | | | | 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 |
zTitle = mprintf("Copy Of %s", zTitle);
zOwner = g.zLogin;
}
}
if( zOwner==0 ) zOwner = g.zLogin;
style_submenu_element("Cancel", "Cancel", "reportlist");
if( rn>0 ){
style_submenu_element("Delete", "Delete", "rptedit?rn=%d&del1=1", rn);
}
style_header(rn>0 ? "Edit Report Format":"Create New Report Format");
if( zErr ){
@ <blockquote><font color="#ff0000"><b>%h(zErr)</b></font></blockquote>
}
@ <form action="rptedit" method="POST">
@ <input type="hidden" name="rn" value="%d(rn)">
@ <p>Report Title:<br />
@ <input type="text" name="t" value="%h(zTitle)" size="60"></p>
@ <p>Enter a complete SQL query statement against the "TICKET" table:<br />
@ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
@ </p>
login_insert_csrf_secret();
if( g.okAdmin ){
@ <p>Report owner:
@ <input type="text" name="w" size="20" value="%h(zOwner)">
@ </p>
} else {
@ <input type="hidden" name="w" value="%h(zOwner)">
}
@ <p>Enter an optional color key in the following box. (If blank, no
@ color key is displayed.) Each line contains the text for a single
@ entry in the key. The first token of each line is the background
@ color for that line.<br />
@ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
@ </p>
if( !g.okAdmin && strcmp(zOwner,g.zLogin)!=0 ){
@ <p>This report format is owned by %h(zOwner). You are not allowed
@ to change it.</p>
@ </form>
report_format_hints();
|
| ︙ | ︙ | |||
446 447 448 449 450 451 452 |
static void report_format_hints(void){
char *zSchema;
zSchema = db_text(0,"SELECT sql FROM sqlite_master WHERE name='ticket'");
if( zSchema==0 ){
zSchema = db_text(0,"SELECT sql FROM repository.sqlite_master"
" WHERE name='ticket'");
}
| | | 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
static void report_format_hints(void){
char *zSchema;
zSchema = db_text(0,"SELECT sql FROM sqlite_master WHERE name='ticket'");
if( zSchema==0 ){
zSchema = db_text(0,"SELECT sql FROM repository.sqlite_master"
" WHERE name='ticket'");
}
@ <hr /><h3>TICKET Schema</h3>
@ <blockquote><pre>
@ %h(zSchema)
@ </pre></blockquote>
@ <h3>Notes</h3>
@ <ul>
@ <li><p>The SQL must consist of a single SELECT statement</p></li>
@
|
| ︙ | ︙ | |||
827 828 829 830 831 832 833 |
zToFree = zSafeKey = mprintf("%h", zClrKey);
while( zSafeKey[0] ){
while( isspace(*zSafeKey) ) zSafeKey++;
for(i=0; zSafeKey[i] && !isspace(zSafeKey[i]); i++){}
for(j=i; isspace(zSafeKey[j]); j++){}
for(k=j; zSafeKey[k] && zSafeKey[k]!='\n' && zSafeKey[k]!='\r'; k++){}
if( !horiz ){
| | | | 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 |
zToFree = zSafeKey = mprintf("%h", zClrKey);
while( zSafeKey[0] ){
while( isspace(*zSafeKey) ) zSafeKey++;
for(i=0; zSafeKey[i] && !isspace(zSafeKey[i]); i++){}
for(j=i; isspace(zSafeKey[j]); j++){}
for(k=j; zSafeKey[k] && zSafeKey[k]!='\n' && zSafeKey[k]!='\r'; k++){}
if( !horiz ){
cgi_printf("<tr style=\"background-color: %.*s;\"><td>%.*s</td></tr>\n",
i, zSafeKey, k-j, &zSafeKey[j]);
}else{
cgi_printf("<td style=\"background-color: %.*s;\">%.*s</td>\n",
i, zSafeKey, k-j, &zSafeKey[j]);
}
zSafeKey += k;
}
free(zToFree);
if( horiz ){
@ </tr>
|
| ︙ | ︙ | |||
905 906 907 908 909 910 911 |
count = 0;
if( !tabs ){
struct GenerateHTML sState;
db_multi_exec("PRAGMA empty_result_callbacks=ON");
style_submenu_element("Raw", "Raw",
| | | | | 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 |
count = 0;
if( !tabs ){
struct GenerateHTML sState;
db_multi_exec("PRAGMA empty_result_callbacks=ON");
style_submenu_element("Raw", "Raw",
"rptview?tablist=1&%s", PD("QUERY_STRING",""));
if( g.okAdmin
|| (g.okTktFmt && g.zLogin && zOwner && strcmp(g.zLogin,zOwner)==0) ){
style_submenu_element("Edit", "Edit", "rptedit?rn=%d", rn);
}
if( g.okTktFmt ){
style_submenu_element("SQL", "SQL", "rptsql?rn=%d",rn);
}
if( g.okNewTkt ){
style_submenu_element("New Ticket", "Create a new ticket",
"%s/tktnew", g.zTop);
}
style_header(zTitle);
output_color_key(zClrKey, 1,
"border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\"");
@ <table border="1" cellpadding="2" cellspacing="0" class="report">
sState.rn = rn;
sState.nCount = 0;
sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
sqlite3_exec(g.db, zSql, generate_html, &sState, &zErr2);
sqlite3_set_authorizer(g.db, 0, 0);
@ </table>
if( zErr1 ){
|
| ︙ | ︙ |
Changes to src/rss.c.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to create a RSS feed for the CGI interface. */ #include "config.h" | | | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to create a RSS feed for the CGI interface.
*/
#include "config.h"
#include <time.h>
#include "rss.h"
#include <assert.h>
/*
** WEBPAGE: timeline.rss
*/
void page_timeline_rss(void){
Stmt q;
int nLine=0;
|
| ︙ | ︙ |
Changes to src/setup.c.
| ︙ | ︙ | |||
97 98 99 100 101 102 103 |
if( !g.okAdmin ){
login_needed();
return;
}
style_submenu_element("Add", "Add User", "setup_uedit");
style_header("User List");
| | | | < | | | | | | | | < | | | | | 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 |
if( !g.okAdmin ){
login_needed();
return;
}
style_submenu_element("Add", "Add User", "setup_uedit");
style_header("User List");
@ <table class="usetupLayoutTable">
@ <tr><td class="usetupColumnLayout">
@ <span class="note">Users:</span>
@ <table class="usetupUserList">
@ <tr>
@ <th class="usetupListUser" style="text-align: right;padding-right: 20px;">User ID</th>
@ <th class="usetupListCap" style="text-align: center;padding-right: 15px;">Capabilities</th>
@ <th class="usetupListCon" style="text-align: left;">Contact Info</th>
@ </tr>
db_prepare(&s, "SELECT uid, login, cap, info FROM user ORDER BY login");
while( db_step(&s)==SQLITE_ROW ){
const char *zCap = db_column_text(&s, 2);
if( strstr(zCap, "s") ) zCap = "s";
@ <tr>
@ <td class="usetupListUser" style="text-align: right;padding-right: 20px;white-space:nowrap;">
if( g.okAdmin && (zCap[0]!='s' || g.okSetup) ){
@ <a href="setup_uedit?id=%d(db_column_int(&s,0))">
}
@ %h(db_column_text(&s,1))
if( g.okAdmin ){
@ </a>
}
@ </td>
@ <td class="usetupListCap" style="text-align: center;padding-right: 15px;">%s(zCap)</td>
@ <td class="usetupListCon" style="text-align: left;">%s(db_column_text(&s,3))</td>
@ </tr>
}
@ </table>
@ </td><td class="usetupColumnLayout">
@ <span class="note">Notes:</span>
@ <ol>
@ <li><p>The permission flags are as follows:</p>
@ <table>
@ <tr><td valign="top"><b>a</b></td>
@ <td><i>Admin:</i> Create and delete users</td></tr>
@ <tr><td valign="top"><b>b</b></td>
@ <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr>
|
| ︙ | ︙ | |||
179 180 181 182 183 184 185 |
@ <tr><td valign="top"><b>v</b></td>
@ <td><i>Developer:</i> Inherit privileges of
@ user <tt>developer</tt></td></tr>
@ <tr><td valign="top"><b>w</b></td>
@ <td><i>Write-Tkt:</i> Edit tickets</td></tr>
@ <tr><td valign="top"><b>z</b></td>
@ <td><i>Zip download:</i> Download a baseline via the
| | > | | > | | | | | | > | > | 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 |
@ <tr><td valign="top"><b>v</b></td>
@ <td><i>Developer:</i> Inherit privileges of
@ user <tt>developer</tt></td></tr>
@ <tr><td valign="top"><b>w</b></td>
@ <td><i>Write-Tkt:</i> Edit tickets</td></tr>
@ <tr><td valign="top"><b>z</b></td>
@ <td><i>Zip download:</i> Download a baseline via the
@ <tt>/zip</tt> URL even without
@ check<span class="capability">o</span>ut
@ and <span class="capability">h</span>istory permissions</td></tr>
@ </table>
@ </li>
@
@ <li><p>
@ Every user, logged in or not, inherits the privileges of
@ <span class="usertype">nobody</span>.
@ </p></li>
@
@ <li><p>
@ Any human can login as <span class="usertype">anonymous</span> since the
@ password is clearly displayed on the login page for them to type. The
@ purpose of requiring anonymous to log in is to prevent access by spiders.
@ Every logged-in user inherits the combined privileges of
@ <span class="usertype">anonymous</span> and
@ <span class="usertype">nobody</span>.
@ </p></li>
@
@ <li><p>
@ Users with privilege <span class="capability">v</span> inherit the combined
@ privileges of <span class="usertype">developer</span>,
@ <span class="usertype">anonymous</span>, and
@ <span class="usertype">nobody</span>.
@ </p></li>
@
@ </ol>
@ </td></tr></table>
style_footer();
}
|
| ︙ | ︙ | |||
321 322 323 324 325 326 327 |
}else{
zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
}
if( uid>0 &&
db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
){
style_header("User Creation Error");
| | | | 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
}else{
zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
}
if( uid>0 &&
db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
){
style_header("User Creation Error");
@ <span class="loginError">Login "%h(zLogin)" is already used by
@ a different user.</span>
@
@ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p>
style_footer();
return;
}
login_verify_csrf_secret();
db_multi_exec(
|
| ︙ | ︙ | |||
351 352 353 354 355 356 357 |
oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
if( uid ){
zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
| | | | | | | | | | | | | | | | | | | | | | | | > | > | > | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > | | | | | | > | | > | | | > | | | | | | > | | | | | > | > | | | > | > | | | > > | > > | > | | | | | > | > | | | > > | | | > | | | | | > | | | | | | | | > | | | | > | > | | > | < | 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 |
oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
if( uid ){
zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
if( strchr(zCap, 'a') ) oaa = " checked=\"checked\"";
if( strchr(zCap, 'b') ) oab = " checked=\"checked\"";
if( strchr(zCap, 'c') ) oac = " checked=\"checked\"";
if( strchr(zCap, 'd') ) oad = " checked=\"checked\"";
if( strchr(zCap, 'e') ) oae = " checked=\"checked\"";
if( strchr(zCap, 'f') ) oaf = " checked=\"checked\"";
if( strchr(zCap, 'g') ) oag = " checked=\"checked\"";
if( strchr(zCap, 'h') ) oah = " checked=\"checked\"";
if( strchr(zCap, 'i') ) oai = " checked=\"checked\"";
if( strchr(zCap, 'j') ) oaj = " checked=\"checked\"";
if( strchr(zCap, 'k') ) oak = " checked=\"checked\"";
if( strchr(zCap, 'm') ) oam = " checked=\"checked\"";
if( strchr(zCap, 'n') ) oan = " checked=\"checked\"";
if( strchr(zCap, 'o') ) oao = " checked=\"checked\"";
if( strchr(zCap, 'p') ) oap = " checked=\"checked\"";
if( strchr(zCap, 'r') ) oar = " checked=\"checked\"";
if( strchr(zCap, 's') ) oas = " checked=\"checked\"";
if( strchr(zCap, 't') ) oat = " checked=\"checked\"";
if( strchr(zCap, 'u') ) oau = " checked=\"checked\"";
if( strchr(zCap, 'v') ) oav = " checked=\"checked\"";
if( strchr(zCap, 'w') ) oaw = " checked=\"checked\"";
if( strchr(zCap, 'z') ) oaz = " checked=\"checked\"";
}
/* figure out inherited permissions */
memset(inherit, 0, sizeof(inherit));
if( strcmp(zLogin, "developer") ){
char *z1, *z2;
z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'");
while( z1 && *z1 ){
inherit[0x7f & *(z1++)] =
"<span class=\"ueditInheritDeveloper\">•</span>";
}
free(z2);
}
if( strcmp(zLogin, "reader") ){
char *z1, *z2;
z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='reader'");
while( z1 && *z1 ){
inherit[0x7f & *(z1++)] =
"<span class=\"ueditInheritReader\">•</span>";
}
free(z2);
}
if( strcmp(zLogin, "anonymous") ){
char *z1, *z2;
z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='anonymous'");
while( z1 && *z1 ){
inherit[0x7f & *(z1++)] =
"<span class=\"ueditInheritAnonymous\">•</span>";
}
free(z2);
}
if( strcmp(zLogin, "nobody") ){
char *z1, *z2;
z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='nobody'");
while( z1 && *z1 ){
inherit[0x7f & *(z1++)] =
"<span class=\"ueditInheritNobody\">•</span>";
}
free(z2);
}
/* Begin generating the page
*/
style_submenu_element("Cancel", "Cancel", "setup_ulist");
if( uid ){
style_header(mprintf("Edit User %h", zLogin));
}else{
style_header("Add A New User");
}
@ <div class="ueditCapBox">
@ <form action="%s(g.zPath)" method="post"><div>
login_insert_csrf_secret();
@ <table>
@ <tr>
@ <td class="usetupEditLabel">User ID:</td>
if( uid ){
@ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)" /></td>
}else{
@ <td>(new user)<input type="hidden" name="id" value="0" /></td>
}
@ </tr>
@ <tr>
@ <td class="usetupEditLabel">Login:</td>
@ <td><input type="text" name="login" value="%h(zLogin)" /></td>
@ </tr>
@ <tr>
@ <td class="usetupEditLabel">Contact Info:</td>
@ <td><input type="text" name="info" size="40" value="%h(zInfo)" /></td>
@ </tr>
@ <tr>
@ <td class="usetupEditLabel">Capabilities:</td>
@ <td>
#define B(x) inherit[x]
if( g.okSetup ){
@ <input type="checkbox" name="as"%s(oas) />%s(B('s'))Setup<br />
}
@ <input type="checkbox" name="aa"%s(oaa) />%s(B('a'))Admin<br />
@ <input type="checkbox" name="ad"%s(oad) />%s(B('d'))Delete<br />
@ <input type="checkbox" name="ae"%s(oae) />%s(B('e'))Email<br />
@ <input type="checkbox" name="ap"%s(oap) />%s(B('p'))Password<br />
@ <input type="checkbox" name="ai"%s(oai) />%s(B('i'))Check-In<br />
@ <input type="checkbox" name="ao"%s(oao) />%s(B('o'))Check-Out<br />
@ <input type="checkbox" name="ah"%s(oah) />%s(B('h'))History<br />
@ <input type="checkbox" name="au"%s(oau) />%s(B('u'))Reader<br />
@ <input type="checkbox" name="av"%s(oav) />%s(B('v'))Developer<br />
@ <input type="checkbox" name="ag"%s(oag) />%s(B('g'))Clone<br />
@ <input type="checkbox" name="aj"%s(oaj) />%s(B('j'))Read Wiki<br />
@ <input type="checkbox" name="af"%s(oaf) />%s(B('f'))New Wiki<br />
@ <input type="checkbox" name="am"%s(oam) />%s(B('m'))Append Wiki<br />
@ <input type="checkbox" name="ak"%s(oak) />%s(B('k'))Write Wiki<br />
@ <input type="checkbox" name="ab"%s(oab) />%s(B('b'))Attachments<br />
@ <input type="checkbox" name="ar"%s(oar) />%s(B('r'))Read Ticket<br />
@ <input type="checkbox" name="an"%s(oan) />%s(B('n'))New Ticket<br />
@ <input type="checkbox" name="ac"%s(oac) />%s(B('c'))Append Ticket<br />
@ <input type="checkbox" name="aw"%s(oaw) />%s(B('w'))Write Ticket<br />
@ <input type="checkbox" name="at"%s(oat) />%s(B('t'))Ticket Report<br />
@ <input type="checkbox" name="az"%s(oaz) />%s(B('z'))Download Zip
@ </td>
@ </tr>
@ <tr>
@ <td align="right">Password:</td>
if( zPw[0] ){
/* Obscure the password for all users */
@ <td><input type="password" name="pw" value="**********" /></td>
}else{
/* Show an empty password as an empty input field */
@ <td><input type="password" name="pw" value="" /></td>
}
@ </tr>
if( !higherUser ){
@ <tr>
@ <td> </td>
@ <td><input type="submit" name="submit" value="Apply Changes" /></td>
@ </tr>
}
@ </table>
@ </div></form>
@ </div>
@ <h2>Privileges And Capabilities:</h2>
@ <ul>
if( higherUser ){
@ <li><p><font color="blue"><b>
@ User %h(zLogin) has Setup privileges and you only have Admin privileges
@ so you are not permitted to make changes to %h(zLogin).
@ </b></font></p></li>
@
}
@ <li><p>
@ The <span class="capability">Setup</span> user can make arbitrary
@ configuration changes. An <span class="usertype">Admin</span> user
@ can add other users and change user privileges
@ and reset user passwords. Both automatically get all other privileges
@ listed below. Use these two settings with discretion.
@ </p></li>
@
@ <li><p>
@ The "<span class="ueditInheritNobody"><big>•</big></span>" mark
@ indicates the privileges of <span class="usertype">nobody</span> that
@ are available to all users regardless of whether or not they are logged in.
@ </p></li>
@
@ <li><p>
@ The "<span class="ueditInheritAnonymous"><big>•</big></span>" mark
@ indicates the privileges of <span class="usertype">anonymous</span> that
@ are inherited by all logged-in users.
@ </p></li>
@
@ <li><p>
@ The "<span class="ueditInheritDeveloper"><big>•</big></span>" mark
@ indicates the privileges of <span class="usertype">developer</span> that
@ are inherited by all users with the
@ <span class="capability">Developer</span> privilege.
@ </p></li>
@
@ <li><p>
@ The "<span class="ueditInheritReader"><big>•</big></span>" mark
@ indicates the privileges of <span class="usertype">reader</span> that
@ are inherited by all users with the <span class="capability">Reader</span>
@ privilege.
@ </p></li>
@
@ <li><p>
@ The <span class="capability">Delete</span> privilege give the user the
@ ability to erase wiki, tickets, and attachments that have been added
@ by anonymous users. This capability is intended for deletion of spam.
@ The delete capability is only in effect for 24 hours after the item
@ is first posted. The <span class="usertype">Setup</span> user can
@ delete anything at any time.
@ </p></li>
@
@ <li><p>
@ The <span class="capability">History</span> privilege allows a user
@ to see most hyperlinks. This is recommended ON for most logged-in users
@ but OFF for user "nobody" to avoid problems with spiders trying to walk
@ every historical version of every baseline and file.
@ </p></li>
@
@ <li><p>
@ The <span class="capability">Zip</span> privilege allows a user to
@ see the "download as ZIP"
@ hyperlink and permits access to the <tt>/zip</tt> page. This allows
@ users to download ZIP archives without granting other rights like
@ <span class="capability">Read</span> or
@ <span class="capability">History</span>. This privilege is recommended for
@ user <span class="usertype">nobody</span> so that automatic package
@ downloaders can obtain the sources without going through the login
@ procedure.
@ </p></li>
@
@ <li><p>
@ The <span class="capability">Check-in</span> privilege allows remote
@ users to "push". The <span class="capability">Check-out</span> privilege
@ allows remote users to "pull". The <span class="capability">Clone</span>
@ privilege allows remote users to "clone".
@ </p></li>
@
@ <li><p>
@ The <span class="capability">Read Wiki</span>,
@ <span class="capability">New Wiki</span>,
@ <span class="capability">Append Wiki</span>, and
@ <b>Write Wiki</b> privileges control access to wiki pages. The
@ <span class="capability">Read Ticket</span>,
@ <span class="capability">New Ticket</span>,
@ <span class="capability">Append Ticket</span>, and
@ <span class="capability">Write Ticket</span> privileges control access
@ to trouble tickets.
@ The <span class="capability">Ticket Report</span> privilege allows
@ the user to create or edit ticket report formats.
@ </p></li>
@
@ <li><p>
@ Users with the <span class="capability">Password</span> privilege
@ are allowed to change their own password. Recommended ON for most
@ users but OFF for special users <span class="usertype">developer</span>,
@ <span class="usertype">anonymous</span>,
@ and <span class="usertype">nobody</span>.
@ </p></li>
@
@ <li><p>
@ The <span class="capability">EMail</span> privilege allows the display of
@ sensitive information such as the email address of users and contact
@ information on tickets. Recommended OFF for
@ <span class="usertype">anonymousy</span> and for
@ <span class="usertype">nobody</span> but ON for
@ <span class="usertype">developer</span>.
@ </p></li>
@
@ <li><p>
@ The <span class="capability">Attachment</span> privilege is needed in
@ order to add attachments to tickets or wiki. Write privilege on the
@ ticket or wiki is also required.
@ </p></li>
@
@ <li><p>
@ Login is prohibited if the password is an empty string.
@ </p></li>
@ </ul>
@
@ <h2>Special Logins</h2>
@
@ <ul>
@ <li><p>
@ No login is required for user <span class="usertype">nobody</span>. The
@ capabilities of the <span class="usertype">nobody</span> user are
@ inherited by all users, regardless of whether or not they are logged in.
@ To disable universal access to the repository, make sure no user named
@ <span class="usertype">nobody</span> exists or that the
@ <span class="usertype">nobody</span> user has no capabilities
@ enabled. The password for <span class="usertype">nobody</span> is ignore.
@ To avoid problems with spiders overloading the server, it is recommended
@ that the <span class="capability">h</span> (History) capability be turned
@ off for the <span class="usertype">nobody</span> user.
@ </p></li>
@
@ <li><p>
@ Login is required for user <span class="usertype">anonymous</span> but the
@ password is displayed on the login screen beside the password entry box
@ so anybody who can read should be able to login as anonymous.
@ On the other hand, spiders and web-crawlers will typically not
@ be able to login. Set the capabilities of the
@ <span class="usertype">anonymous</span>
@ user to things that you want any human to be able to do, but not any
@ spider. Every other logged-in user inherits the privileges of
@ <span class="usertype">anonymous</span>.
@ </p></li>
@
@ <li><p>
@ The <span class="usertype">developer</span> user is intended as a template
@ for trusted users with check-in privileges. When adding new trusted users,
@ simply select the <span class="capability">developer</span> privilege to
@ cause the new user to inherit all privileges of the
@ <span class="usertype">developer</span>
@ user. Similarly, the <span class="usertype">reader</span> user is a
@ template for users who are allowed more access than
@ <span class="usertype">anonymous</span>,
@ but less than a <span class="usertype">developer</span>.
@ </p></li>
@ </ul>
style_footer();
}
/*
** Generate a checkbox for an attribute.
*/
|
| ︙ | ︙ | |||
649 650 651 652 653 654 655 |
if( iQ!=iVal ){
login_verify_csrf_secret();
db_set(zVar, iQ ? "1" : "0", 0);
iVal = iQ;
}
}
if( iVal ){
| | > | | | 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 |
if( iQ!=iVal ){
login_verify_csrf_secret();
db_set(zVar, iQ ? "1" : "0", 0);
iVal = iQ;
}
}
if( iVal ){
@ <input type="checkbox" name="%s(zQParm)" checked="checked" />
@ <b>%s(zLabel)</b>
}else{
@ <input type="checkbox" name="%s(zQParm)" /><b>%s(zLabel)</b>
}
}
/*
** Generate an entry box for an attribute.
*/
void entry_attribute(
const char *zLabel, /* The text label on the entry box */
int width, /* Width of the entry box */
const char *zVar, /* The corresponding row in the VAR table */
const char *zQParm, /* The query parameter */
char *zDflt /* Default value if VAR table entry does not exist */
){
const char *zVal = db_get(zVar, zDflt);
const char *zQ = P(zQParm);
if( zQ && strcmp(zQ,zVal)!=0 ){
login_verify_csrf_secret();
db_set(zVar, zQ, 0);
zVal = zQ;
}
@ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" />
@ <b>%s(zLabel)</b>
}
/*
** Generate a text box for an attribute.
*/
static void textarea_attribute(
|
| ︙ | ︙ | |||
696 697 698 699 700 701 702 |
if( zQ && strcmp(zQ,z)!=0 ){
login_verify_csrf_secret();
db_set(zVar, zQ, 0);
z = zQ;
}
if( rows>0 && cols>0 ){
@ <textarea name="%s(zQP)" rows="%d(rows)" cols="%d(cols)">%h(z)</textarea>
| > | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | 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 |
if( zQ && strcmp(zQ,z)!=0 ){
login_verify_csrf_secret();
db_set(zVar, zQ, 0);
z = zQ;
}
if( rows>0 && cols>0 ){
@ <textarea name="%s(zQP)" rows="%d(rows)" cols="%d(cols)">%h(z)</textarea>
if (zLabel && *zLabel)
@ <span class="textareaLabel">%s(zLabel)</span>
}
}
/*
** WEBPAGE: setup_access
*/
void setup_access(void){
login_check_credentials();
if( !g.okSetup ){
login_needed();
}
style_header("Access Control Settings");
db_begin_transaction();
@ <form action="%s(g.zBaseURL)/setup_access" method="post"><div>
login_insert_csrf_secret();
@ <hr />
onoff_attribute("Require password for local access",
"localauth", "localauth", 0);
@ <p>When enabled, the password sign-in is required for
@ web access coming from 127.0.0.1. When disabled, web access
@ from 127.0.0.1 is allows without any login - the user id is selected
@ from the ~/.fossil database. Password login is always required
@ for incoming web connections on internet addresses other than
@ 127.0.0.1.</p>
@ <hr />
onoff_attribute("Allow REMOTE_USER authentication",
"remote_user_ok", "remote_user_ok", 0);
@ <p>When enabled, if the REMOTE_USER environment variable is set to the
@ login name of a valid user and no other login credentials are available,
@ then the REMOTE_USER is accepted as an authenticated user.
@ </p>
@ <hr />
entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
@ <p>The number of hours for which a login is valid. This must be a
@ positive number. The default is 8760 hours which is approximately equal
@ to a year.</p>
@ <hr />
entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
"5000000");
@ <p>Fossil tries to limit out-bound sync, clone, and pull packets
@ to this many bytes, uncompressed. If the client requires more data
@ than this, then the client will issue multiple HTTP requests.
@ Values below 1 million are not recommended. 5 million is a
@ reasonable number.</p>
@ <hr />
onoff_attribute("Show javascript button to fill in CAPTCHA",
"auto-captcha", "autocaptcha", 0);
@ <p>When enabled, a button appears on the login screen for user
@ "anonymous" that will automatically fill in the CAPTCHA password.
@ This is less secure that forcing the user to do it manually, but is
@ probably secure enough and it is certainly more convenient for
@ anonymous users.</p>
@ <hr />
@ <p><input type="submit" name="submit" value="Apply Changes" /></p>
@ </div></form>
db_end_transaction(0);
style_footer();
}
/*
** WEBPAGE: setup_timeline
*/
void setup_timeline(void){
login_check_credentials();
if( !g.okSetup ){
login_needed();
}
style_header("Timeline Display Preferences");
db_begin_transaction();
@ <form action="%s(g.zBaseURL)/setup_timeline" method="post"><div>
login_insert_csrf_secret();
@ <hr />
onoff_attribute("Allow block-markup in timeline",
"timeline-block-markup", "tbm", 0);
@ <p>In timeline displays, check-in comments can be displayed with or
@ without block markup (paragraphs, tables, etc.)</p>
@ <hr />
onoff_attribute("Use Universal Coordinated Time (UTC)",
"timeline-utc", "utc", 1);
@ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
@ Zulu) instead of in local time.</p>
@ <hr />
onoff_attribute("Show version differences by default",
"show-version-diffs", "vdiff", 0);
@ <p>On the version-information pages linked from the timeline can either
@ show complete diffs of all file changes, or can just list the names of
@ the files that have changed. Users can get to either page by
@ clicking. This setting selects the default.</p>
@ <hr />
entry_attribute("Max timeline comment length", 6,
"timeline-max-comment", "tmc", "0");
@ <p>The maximum length of a comment to be displayed in a timeline.
@ "0" there is no length limit.</p>
@ <hr />
@ <p><input type="submit" name="submit" value="Apply Changes" /></p>
@ </div></form>
db_end_transaction(0);
style_footer();
}
/*
** WEBPAGE: setup_config
*/
void setup_config(void){
login_check_credentials();
if( !g.okSetup ){
login_needed();
}
style_header("WWW Configuration");
db_begin_transaction();
@ <form action="%s(g.zBaseURL)/setup_config" method="post"><div>
login_insert_csrf_secret();
@ <hr />
entry_attribute("Project Name", 60, "project-name", "pn", "");
@ <p>Give your project a name so visitors know what this site is about.
@ The project name will also be used as the RSS feed title.</p>
@ <hr />
textarea_attribute("Project Description", 5, 60,
"project-description", "pd", "");
@ <p>Describe your project. This will be used in page headers for search
@ engines as well as a short RSS description.</p>
@ <hr />
entry_attribute("Index Page", 60, "index-page", "idxpg", "/home");
@ <p>Enter the pathname of the page to display when the "Home" menu
@ option is selected and when no pathname is
@ specified in the URL. For example, if you visit the url:</p>
@
@ <blockquote><p>%h(g.zBaseURL)</p></blockquote>
@
@ <p>And you have specified an index page of "/home" the above will
@ automatically redirect to:</p>
@
@ <blockquote><p>%h(g.zBaseURL)/home</p></blockquote>
@
@ <p>The default "/home" page displays a Wiki page with the same name
@ as the Project Name specified above. Some sites prefer to redirect
@ to a documentation page (ex: "/doc/tip/index.wiki") or to "/timeline".</p>
@ <hr />
onoff_attribute("Use HTML as wiki markup language",
"wiki-use-html", "wiki-use-html", 0);
@ <p>Use HTML as the wiki markup language. Wiki links will still be parsed
@ but all other wiki formatting will be ignored. This option is helpful
@ if you have chosen to use a rich HTML editor for wiki markup such as
@ TinyMCE.</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 publically
@ editable wiki.</p>
@ <hr />
@ <p><input type="submit" name="submit" value="Apply Changes" /></p>
@ </div></form>
db_end_transaction(0);
style_footer();
}
/*
** WEBPAGE: setup_editcss
*/
|
| ︙ | ︙ | |||
890 891 892 893 894 895 896 |
textarea_attribute(0, 0, 0, "css", "css", zDefaultCSS);
}
if( P("submit")!=0 ){
db_end_transaction(0);
cgi_redirect("setup_editcss");
}
style_header("Edit CSS");
| | | | | | | | | | 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 |
textarea_attribute(0, 0, 0, "css", "css", zDefaultCSS);
}
if( P("submit")!=0 ){
db_end_transaction(0);
cgi_redirect("setup_editcss");
}
style_header("Edit CSS");
@ <form action="%s(g.zBaseURL)/setup_editcss" method="post"><div>
login_insert_csrf_secret();
@ Edit the CSS below:<br />
textarea_attribute("", 40, 80, "css", "css", zDefaultCSS);
@ <br />
@ <input type="submit" name="submit" value="Apply Changes" />
@ <input type="submit" name="clear" value="Revert To Default" />
@ </div></form>
@ <p><span class="note">Note:</span> Press your browser Reload button after
@ modifying the CSS in order to pull in the modified CSS file.</p>
@ <hr />
@ The default CSS is shown below for reference. Other examples
@ of CSS files can be seen on the <a href="setup_skin">skins page</a>.
@ See also the <a href="setup_header">header</a> and
@ <a href="setup_footer">footer</a> editing screens.
@ <blockquote><pre>
cgi_append_default_css();
@ </pre></blockquote>
style_footer();
db_end_transaction(0);
}
/*
** WEBPAGE: setup_header
|
| ︙ | ︙ | |||
928 929 930 931 932 933 934 |
if( P("clear")!=0 ){
db_multi_exec("DELETE FROM config WHERE name='header'");
cgi_replace_parameter("header", zDefaultHeader);
}else{
textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader);
}
style_header("Edit Page Header");
| | | | | | | 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 |
if( P("clear")!=0 ){
db_multi_exec("DELETE FROM config WHERE name='header'");
cgi_replace_parameter("header", zDefaultHeader);
}else{
textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader);
}
style_header("Edit Page Header");
@ <form action="%s(g.zBaseURL)/setup_header" method="post"><div>
login_insert_csrf_secret();
@ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
@ generate the beginning of every page through start of the main
@ menu.</p>
textarea_attribute("", 40, 80, "header", "header", zDefaultHeader);
@ <br />
@ <input type="submit" name="submit" value="Apply Changes" />
@ <input type="submit" name="clear" value="Revert To Default" />
@ </div></form>
@ <hr />
@ The default header is shown below for reference. Other examples
@ of headers can be seen on the <a href="setup_skin">skins page</a>.
@ See also the <a href="setup_editcss">CSS</a> and
@ <a href="setup_footer">footer</a> editing screeens.
@ <blockquote><pre>
@ %h(zDefaultHeader)
@ </pre></blockquote>
|
| ︙ | ︙ | |||
966 967 968 969 970 971 972 |
if( P("clear")!=0 ){
db_multi_exec("DELETE FROM config WHERE name='footer'");
cgi_replace_parameter("footer", zDefaultFooter);
}else{
textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter);
}
style_header("Edit Page Footer");
| | | | | | | 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 |
if( P("clear")!=0 ){
db_multi_exec("DELETE FROM config WHERE name='footer'");
cgi_replace_parameter("footer", zDefaultFooter);
}else{
textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter);
}
style_header("Edit Page Footer");
@ <form action="%s(g.zBaseURL)/setup_footer" method="post"><div>
login_insert_csrf_secret();
@ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
@ generate the end of every page.</p>
textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter);
@ <br />
@ <input type="submit" name="submit" value="Apply Changes" />
@ <input type="submit" name="clear" value="Revert To Default" />
@ </div></form>
@ <hr />
@ The default footer is shown below for reference. Other examples
@ of footers can be seen on the <a href="setup_skin">skins page</a>.
@ See also the <a href="setup_editcss">CSS</a> and
@ <a href="setup_header">header</a> editing screens.
@ <blockquote><pre>
@ %h(zDefaultFooter)
@ </pre></blockquote>
|
| ︙ | ︙ | |||
1029 1030 1031 1032 1033 1034 1035 |
);
db_end_transaction(0);
cgi_redirect("setup_logo");
}
style_header("Edit Project Logo");
@ <p>The current project logo has a MIME-Type of <b>%h(zMime)</b> and looks
@ like this:</p>
| | | | | | | | | | | | | 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 |
);
db_end_transaction(0);
cgi_redirect("setup_logo");
}
style_header("Edit Project Logo");
@ <p>The current project logo has a MIME-Type of <b>%h(zMime)</b> and looks
@ like this:</p>
@ <blockquote><p><img src="%s(g.zTop)/logo" alt="logo" /></p></blockquote>
@
@ <p>The logo is accessible to all users at this URL:
@ <a href="%s(g.zBaseURL)/logo">%s(g.zBaseURL)/logo</a>.
@ The logo may or may not appear on each
@ page depending on the <a href="setup_editcss">CSS</a> and
@ <a href="setup_header">header setup</a>.</p>
@
@ <form action="%s(g.zBaseURL)/setup_logo" method="post"
@ enctype="multipart/form-data"><div>
@ <p>To set a new logo image, select a file to use as the logo using
@ the entry box below and then press the "Change Logo" button.</p>
login_insert_csrf_secret();
@ Logo Image file:
@ <input type="file" name="im" size="60" accept="image/*" /><br />
@ <input type="submit" name="set" value="Change Logo" />
@ <input type="submit" name="clr" value="Revert To Default" />
@ </div></form>
@
@ <p><span class="note">Note:</span> Your browser has probably cached the
@ logo image, so you will probably need to press the Reload button on your
@ browser after changing the logo to provoke your browser to reload the new
@ logo image. </p>
style_footer();
db_end_transaction(0);
}
|
Changes to src/sha1.c.
1 2 3 4 | /* ** This implementation of SHA1 is adapted from the example implementation ** contained in RFC-3174. */ | < < < < < > > | | > > > > > > | < | 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 | /* ** This implementation of SHA1 is adapted from the example implementation ** contained in RFC-3174. */ /* * If you do not have the ISO standard stdint.h header file, then you * must typdef the following: * name meaning * */ #if defined(__DMC__) || defined(_MSC_VER) typedef unsigned long uint32_t; //unsigned 32 bit integer typedef unsigned char uint8_t; //unsigned 8 bit integer (i.e., unsigned char) #else # include <stdint.h> #endif #include <sys/types.h> #include "config.h" #include "sha1.h" #define SHA1HashSize 20 #define shaSuccess 0 #define shaInputTooLong 1 #define shaStateError 2 /* * This structure will hold context information for the SHA-1 |
| ︙ | ︙ |
Changes to src/shun.c.
| ︙ | ︙ | |||
110 111 112 113 114 115 116 | @ from the repository. Inappropriate content includes such things as @ spam added to Wiki, files that violate copyright or patent agreements, @ or artifacts that by design or accident interfere with the processing @ of the repository. Do not shun artifacts merely to remove them from @ sight - set the "hidden" tag on such artifacts instead.</p> @ @ <blockquote> | | | | | | | | | | | | | | | | | | 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 |
@ from the repository. Inappropriate content includes such things as
@ spam added to Wiki, files that violate copyright or patent agreements,
@ or artifacts that by design or accident interfere with the processing
@ of the repository. Do not shun artifacts merely to remove them from
@ sight - set the "hidden" tag on such artifacts instead.</p>
@
@ <blockquote>
@ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div>
login_insert_csrf_secret();
@ <input type="text" name="uuid" value="%h(PD("shun",""))" size="50" />
@ <input type="submit" name="add" value="Shun" />
@ </div></form>
@ </blockquote>
@
@ <p>Enter the UUID of a previous shunned artifact to cause it to be
@ accepted again in the repository. The artifact content is not
@ restored because the content is unknown. The only change is that
@ the formerly shunned artifact will be accepted on subsequent sync
@ operations.</p>
@
@ <blockquote>
@ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div>
login_insert_csrf_secret();
@ <input type="text" name="uuid" size="50" />
@ <input type="submit" name="sub" value="Accept" />
@ </div></form>
@ </blockquote>
@
@ <p>Press the Rebuild button below to rebuild the respository. The
@ content of newly shunned artifacts is not purged until the repository
@ is rebuilt. On larger repositories, the rebuild may take minute or
@ two, so be patient after pressing the button.</p>
@
@ <blockquote>
@ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div>
login_insert_csrf_secret();
@ <input type="submit" name="rebuild" value="Rebuild" />
@ </div></form>
@ </blockquote>
@
@ <hr /><p>Shunned Artifacts:</p>
@ <blockquote><p>
db_prepare(&q,
"SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)"
" FROM shun ORDER BY uuid");
while( db_step(&q)==SQLITE_ROW ){
const char *zUuid = db_column_text(&q, 0);
int stillExists = db_column_int(&q, 1);
cnt++;
if( stillExists ){
@ <b><a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a></b><br />
}else{
@ <b>%s(zUuid)</b><br />
}
}
if( cnt==0 ){
@ <i>no artifacts are shunned on this server</i>
}
db_finalize(&q);
@ </p></blockquote>
style_footer();
}
/*
** Remove from the BLOB table all artifacts that are in the SHUN table.
*/
void shun_artifacts(void){
|
| ︙ | ︙ | |||
227 228 229 230 231 232 233 | @ finding and fixing attempts to inject illicit content into the @ repository.</p> @ @ <p>Click on the "rcvid" to show a list of specific artifacts received @ by a transaction. After identifying illicit artifacts, remove them @ using the "Shun" feature.</p> @ | | | | > | | | | | | 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 |
@ finding and fixing attempts to inject illicit content into the
@ repository.</p>
@
@ <p>Click on the "rcvid" to show a list of specific artifacts received
@ by a transaction. After identifying illicit artifacts, remove them
@ using the "Shun" feature.</p>
@
@ <table cellpadding="0" cellspacing="0" border="0">
@ <tr><th style="padding-right: 15px;text-align: right;">rcvid</th>
@ <th style="padding-right: 15px;text-align: left;">Date</th>
@ <th style="padding-right: 15px;text-align: left;">User</th>
@ <th style="text-align: left;">IP Address</th></tr>
cnt = 0;
while( db_step(&q)==SQLITE_ROW ){
int rcvid = db_column_int(&q, 0);
const char *zUser = db_column_text(&q, 1);
const char *zDate = db_column_text(&q, 2);
const char *zIpAddr = db_column_text(&q, 3);
if( cnt==30 ){
style_submenu_element("Older", "Older",
"rcvfromlist?ofst=%d", ofst+30);
}else{
cnt++;
@ <tr>
@ <td style="padding-right: 15px;text-align: right;"><a href="rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a></td>
@ <td style="padding-right: 15px;text-align: left;">%s(zDate)</td>
@ <td style="padding-right: 15px;text-align: left;">%h(zUser)</td>
@ <td style="text-align: left;">%s(zIpAddr)</td>
@ </tr>
}
}
db_finalize(&q);
@ </table>
style_footer();
}
|
| ︙ | ︙ | |||
275 276 277 278 279 280 281 |
style_header("Content Source %d", rcvid);
db_prepare(&q,
"SELECT login, datetime(rcvfrom.mtime), rcvfrom.ipaddr"
" FROM rcvfrom LEFT JOIN user USING(uid)"
" WHERE rcvid=%d",
rcvid
);
| | | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
style_header("Content Source %d", rcvid);
db_prepare(&q,
"SELECT login, datetime(rcvfrom.mtime), rcvfrom.ipaddr"
" FROM rcvfrom LEFT JOIN user USING(uid)"
" WHERE rcvid=%d",
rcvid
);
@ <table cellspacing="15" cellpadding="0" border="0">
@ <tr><td valign="top" align="right"><b>rcvid:</b></td>
@ <td valign="top">%d(rcvid)</td></tr>
if( db_step(&q)==SQLITE_ROW ){
const char *zUser = db_column_text(&q, 0);
const char *zDate = db_column_text(&q, 1);
const char *zIpAddr = db_column_text(&q, 2);
@ <tr><td valign="top" align="right"><b>User:</b></td>
|
| ︙ | ︙ | |||
300 301 302 303 304 305 306 |
@ <tr><td valign="top" align="right"><b>Artifacts:</b></td>
@ <td valign="top">
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q, 0);
const char *zUuid = db_column_text(&q, 1);
int size = db_column_int(&q, 2);
@ <a href="%s(g.zBaseURL)/info/%s(zUuid)">%s(zUuid)</a>
| | > > | 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
@ <tr><td valign="top" align="right"><b>Artifacts:</b></td>
@ <td valign="top">
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q, 0);
const char *zUuid = db_column_text(&q, 1);
int size = db_column_int(&q, 2);
@ <a href="%s(g.zBaseURL)/info/%s(zUuid)">%s(zUuid)</a>
@ (rid: %d(rid), size: %d(size))<br />
}
@ </td></tr>
@ </table>
db_finalize(&q);
style_footer();
}
|
Changes to src/skins.c.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 | #include "skins.h" /* @-comment: // */ /* ** A black-and-white theme with the project title in a bar across the top ** and no logo image. */ | | | | | | | | | | | | | | | 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 |
#include "skins.h"
/* @-comment: // */
/*
** A black-and-white theme with the project title in a bar across the top
** and no logo image.
*/
static const char zBuiltinSkin1[] =
@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
@ body {
@ margin: 0ex 1ex;
@ padding: 0px;
@ background-color: white;
@ font-family: sans-serif;
@ }
@
@ /* The project logo in the upper left-hand corner of each page */
@ div.logo {
@ display: table-row;
@ text-align: center;
@ /* vertical-align: bottom;*/
@ font-size: 2em;
@ font-weight: bold;
@ background-color: #707070;
@ color: #ffffff;
@ min-width: 200px;
@ }
@
@ /* The page title centered at the top of each page */
@ div.title {
@ display: table-cell;
@ font-size: 1.5em;
@ font-weight: bold;
@ text-align: center;
@ padding: 0 0 0 10px;
@ color: #404040;
@ vertical-align: bottom;
@ width: 100%;
@ }
@
@ /* The login status message in the top right-hand corner */
@ div.status {
@ display: table-cell;
@ text-align: right;
@ vertical-align: bottom;
@ color: #404040;
@ font-size: 0.8em;
@ font-weight: bold;
@ min-width: 200px;
@ }
@
@ /* The header across the top of the page */
@ div.header {
@ display: table;
@ width: 100%;
@ }
@
@ /* The main menu bar that appears at the top of the page beneath
@ ** the header */
@ div.mainmenu {
@ padding: 5px 10px 5px 10px;
@ font-size: 0.9em;
@ font-weight: bold;
@ text-align: center;
@ letter-spacing: 1px;
@ background-color: #404040;
@ color: white;
@ }
@
@ /* The submenu bar that *sometimes* appears below the main menu */
@ div.submenu {
@ padding: 3px 10px 3px 0px;
@ font-size: 0.9em;
@ text-align: center;
@ background-color: #606060;
@ color: white;
@ }
@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited {
@ padding: 3px 10px 3px 10px;
@ color: white;
@ text-decoration: none;
@ }
@ div.mainmenu a:hover, div.submenu a:hover {
@ color: #404040;
@ background-color: white;
@ }
@
@ /* All page content from the bottom of the menu or submenu down to
@ ** the footer */
@ div.content {
@ padding: 0ex 0ex 0ex 0ex;
@ }
@ /* Hyperlink colors */
@ div.content a { color: #604000; }
@ div.content a:link { color: #604000;}
@ div.content a:visited { color: #600000; }
@
@ /* Some pages have section dividers */
@ div.section {
@ margin-bottom: 0px;
@ margin-top: 1em;
@ padding: 1px 1px 1px 1px;
@ font-size: 1.2em;
@ font-weight: bold;
@ background-color: #404040;
@ color: white;
@ }
@
@ /* The "Date" that occurs on the left hand side of timelines */
@ div.divider {
@ background: #a0a0a0;
@ border: 2px #505050 solid;
@ font-size: 1em; font-weight: normal;
@ padding: .25em;
@ margin: .2em 0 .2em 0;
@ float: left;
@ clear: left;
@ }
@
@ /* The footer at the very bottom of the page */
@ div.footer {
@ font-size: 0.8em;
@ margin-top: 12px;
@ padding: 5px 10px 5px 10px;
@ text-align: right;
@ background-color: #404040;
@ color: white;
@ }
@
@ /* The label/value pairs on (for example) the vinfo page */
@ table.label-value th {
@ vertical-align: top;
@ text-align: right;
@ padding: 0.2ex 2ex;
@ }');
@ REPLACE INTO config VALUES('header','<html>
@ <head>
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@ href="$baseurl/timeline.rss">
@ <link rel="stylesheet" href="$baseurl/style.css?blackwhite" type="text/css"
@ media="screen">
@ </head>
@ <body>
@ <div class="header">
@ <div class="logo">
@ <img src="$baseurl/logo" alt="logo">
@ </div>
@ <div class="title"><small>$<project_name></small><br />$<title></div>
@ <div class="status"><nobr><th1>
@ if {[info exists login]} {
@ puts "Logged in as $login"
@ } else {
@ puts "Not logged in"
@ }
@ </th1></nobr></div>
|
| ︙ | ︙ | |||
204 205 206 207 208 209 210 |
@ html "<a href=''$baseurl/login''>Logout</a> "
@ } else {
@ html "<a href=''$baseurl/login''>Login</a> "
@ }
@ </th1></div>
@ ');
@ REPLACE INTO config VALUES('footer','<div class="footer">
| | | | | | | | | | | | | | | | | 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 |
@ html "<a href=''$baseurl/login''>Logout</a> "
@ } else {
@ html "<a href=''$baseurl/login''>Login</a> "
@ }
@ </th1></div>
@ ');
@ REPLACE INTO config VALUES('footer','<div class="footer">
@ Fossil version $manifest_version $manifest_date
@ </div>
@ </body></html>
@ ');
;
/*
** A tan theme with the project title above the user identification
** and no logo image.
*/
static const char zBuiltinSkin2[] =
@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
@ body {
@ margin: 0ex 0ex;
@ padding: 0px;
@ background-color: #fef3bc;
@ font-family: sans-serif;
@ }
@
@ /* The project logo in the upper left-hand corner of each page */
@ div.logo {
@ display: inline;
@ text-align: center;
@ vertical-align: bottom;
@ font-weight: bold;
@ font-size: 2.5em;
@ color: #a09048;
@ }
@
@ /* The page title centered at the top of each page */
@ div.title {
@ display: table-cell;
@ font-size: 2em;
@ font-weight: bold;
@ text-align: left;
@ padding: 0 0 0 5px;
@ color: #a09048;
@ vertical-align: bottom;
@ width: 100%;
@ }
@
@ /* The login status message in the top right-hand corner */
@ div.status {
@ display: table-cell;
@ text-align: right;
@ vertical-align: bottom;
@ color: #a09048;
@ padding: 5px 5px 0 0;
@ font-size: 0.8em;
@ font-weight: bold;
@ }
@
@ /* The header across the top of the page */
@ div.header {
@ display: table;
@ width: 100%;
@ }
@
@ /* The main menu bar that appears at the top of the page beneath
@ ** the header */
@ div.mainmenu {
@ padding: 5px 10px 5px 10px;
@ font-size: 0.9em;
@ font-weight: bold;
@ text-align: center;
@ letter-spacing: 1px;
@ background-color: #a09048;
@ color: black;
@ }
@
@ /* The submenu bar that *sometimes* appears below the main menu */
@ div.submenu {
@ padding: 3px 10px 3px 0px;
@ font-size: 0.9em;
@ text-align: center;
@ background-color: #c0af58;
@ color: white;
@ }
@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited {
@ padding: 3px 10px 3px 10px;
@ color: white;
@ text-decoration: none;
@ }
@ div.mainmenu a:hover, div.submenu a:hover {
@ color: #a09048;
@ background-color: white;
@ }
@
@ /* All page content from the bottom of the menu or submenu down to
@ ** the footer */
@ div.content {
@ padding: 1ex 5px;
@ }
@ div.content a { color: #706532; }
@ div.content a:link { color: #706532; }
@ div.content a:visited { color: #704032; }
@ div.content a:hover { background-color: white; color: #706532; }
@
@ /* Some pages have section dividers */
@ div.section {
@ margin-bottom: 0px;
@ margin-top: 1em;
@ padding: 3px 3px 0 3px;
@ font-size: 1.2em;
@ font-weight: bold;
@ background-color: #a09048;
@ color: white;
@ }
@
@ /* The "Date" that occurs on the left hand side of timelines */
@ div.divider {
@ background: #e1d498;
@ border: 2px #a09048 solid;
@ font-size: 1em; font-weight: normal;
@ padding: .25em;
@ margin: .2em 0 .2em 0;
@ float: left;
@ clear: left;
@ }
@
@ /* The footer at the very bottom of the page */
@ div.footer {
@ font-size: 0.8em;
@ margin-top: 12px;
@ padding: 5px 10px 5px 10px;
@ text-align: right;
@ background-color: #a09048;
@ color: white;
@ }
@
@ /* Hyperlink colors */
@ div.footer a { color: white; }
@ div.footer a:link { color: white; }
@ div.footer a:visited { color: white; }
@ div.footer a:hover { background-color: white; color: #558195; }
@
@ /* <verbatim> blocks */
@ pre.verbatim {
@ background-color: #f5f5f5;
@ padding: 0.5em;
@ }
@
@ /* The label/value pairs on (for example) the ci page */
@ table.label-value th {
@ vertical-align: top;
@ text-align: right;
@ padding: 0.2ex 2ex;
@ }
@ ');
|
| ︙ | ︙ | |||
416 417 418 419 420 421 422 | @ '); ; /* ** Black letters on a white or cream background with the main menu ** stuck on the left-hand side. */ | | | | | | | | | | | | | | | | | | | 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 |
@ ');
;
/*
** Black letters on a white or cream background with the main menu
** stuck on the left-hand side.
*/
static const char zBuiltinSkin3[] =
@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
@ body {
@ margin:0px 0px 0px 0px;
@ padding:0px;
@ font-family:verdana, arial, helvetica, "sans serif";
@ color:#333;
@ background-color:white;
@ }
@
@ /* consistent colours */
@ h2 {
@ color: #333;
@ }
@ h3 {
@ color: #333;
@ }
@
@ /* The project logo in the upper left-hand corner of each page */
@ div.logo {
@ display: table-cell;
@ text-align: left;
@ vertical-align: bottom;
@ font-weight: bold;
@ color: #333;
@ }
@
@ /* The page title centered at the top of each page */
@ div.title {
@ display: table-cell;
@ font-size: 2em;
@ font-weight: bold;
@ text-align: center;
@ color: #333;
@ vertical-align: bottom;
@ width: 100%;
@ }
@
@ /* The login status message in the top right-hand corner */
@ div.status {
@ display: table-cell;
@ padding-right: 10px;
@ text-align: right;
@ vertical-align: bottom;
@ padding-bottom: 5px;
@ color: #333;
@ font-size: 0.8em;
@ font-weight: bold;
@ }
@
@ /* The header across the top of the page */
@ div.header {
@ margin:10px 0px 10px 0px;
@ padding:1px 0px 0px 20px;
@ border-style:solid;
@ border-color:black;
@ border-width:1px 0px;
@ background-color:#eee;
@ }
@
@ /* The main menu bar that appears at the top left of the page beneath
@ ** the header. Width must be co-ordinated with the container below */
@ div.mainmenu {
@ float: left;
@ margin-left: 10px;
@ margin-right: 10px;
@ font-size: 0.9em;
@ font-weight: bold;
@ padding:5px;
@ background-color:#eee;
@ border:1px solid #999;
@ width:8em;
@ }
@
@ /* Main menu is now a list */
@ div.mainmenu ul {
@ padding: 0;
@ list-style:none;
@ }
@ div.mainmenu a, div.mainmenu a:visited{
@ padding: 1px 10px 1px 10px;
@ color: #333;
@ text-decoration: none;
@ }
@ div.mainmenu a:hover {
@ color: #eee;
@ background-color: #333;
@ }
@
@ /* Container for the sub-menu and content so they don''t spread
@ ** out underneath the main menu */
@ #container {
@ padding-left: 9em;
@ }
@
@ /* The submenu bar that *sometimes* appears below the main menu */
@ div.submenu {
@ padding: 3px 10px 3px 10px;
@ font-size: 0.9em;
@ text-align: center;
@ border:1px solid #999;
@ border-width:1px 0px;
@ background-color: #eee;
@ color: #333;
@ }
@ div.submenu a, div.submenu a:visited {
@ padding: 3px 10px 3px 10px;
@ color: #333;
@ text-decoration: none;
@ }
@ div.submenu a:hover {
@ color: #eee;
@ background-color: #333;
@ }
@
@ /* All page content from the bottom of the menu or submenu down to
@ ** the footer */
@ div.content {
@ float right;
@ padding: 2ex 1ex 0ex 2ex;
@ }
@
@ /* Some pages have section dividers */
@ div.section {
@ margin-bottom: 0px;
@ margin-top: 1em;
@ padding: 1px 1px 1px 1px;
@ font-size: 1.2em;
@ font-weight: bold;
@ border-style:solid;
@ border-color:#999;
@ border-width:1px 0px;
@ background-color: #eee;
@ color: #333;
@ }
@
@ /* The "Date" that occurs on the left hand side of timelines */
@ div.divider {
@ background: #eee;
@ border: 2px #999 solid;
@ font-size: 1em; font-weight: normal;
@ padding: .25em;
@ margin: .2em 0 .2em 0;
@ float: left;
@ clear: left;
@ color: #333
@ }
@
@ /* The footer at the very bottom of the page */
@ div.footer {
@ font-size: 0.8em;
@ margin-top: 12px;
@ padding: 5px 10px 5px 10px;
@ text-align: right;
@ background-color: #eee;
@ color: #555;
@ }
@
@ /* <verbatim> blocks */
@ pre.verbatim {
@ background-color: #f5f5f5;
@ padding: 0.5em;
@ }
@
@ /* The label/value pairs on (for example) the ci page */
@ table.label-value th {
@ vertical-align: top;
@ text-align: right;
@ padding: 0.2ex 2ex;
@ }');
@ REPLACE INTO config VALUES('header','<html>
@ <head>
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@ href="$baseurl/timeline.rss">
@ <link rel="stylesheet" href="$baseurl/style.css?black2" type="text/css"
@ media="screen">
@ </head>
@ <body>
@ <div class="header">
@ <div class="logo">
@ <!-- <img src="$baseurl/logo" alt="logo"> -->
@ <br /><nobr>$<project_name></nobr>
@ </div>
@ <div class="title">$<title></div>
@ <div class="status"><nobr><th1>
@ if {[info exists login]} {
@ puts "Logged in as $login"
@ } else {
@ puts "Not logged in"
|
| ︙ | ︙ | |||
730 731 732 733 734 735 736 |
login_needed();
}
db_begin_transaction();
/* Process requests to delete a user-defined skin */
if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
style_header("Confirm Custom Skin Delete");
| | | | | | | 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 |
login_needed();
}
db_begin_transaction();
/* Process requests to delete a user-defined skin */
if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
style_header("Confirm Custom Skin Delete");
@ <form action="%s(g.zBaseURL)/setup_skin" method="post"><div>
@ <p>Deletion of a custom skin is a permanent action that cannot
@ be undone. Please confirm that this is what you want to do:</p>
@ <input type="hidden" name="sn" value="%h(P("sn"))" />
@ <input type="submit" name="del2" value="Confirm - Delete The Skin" />
@ <input type="submit" name="cancel" value="Cancel - Do Not Delete" />
login_insert_csrf_secret();
@ </div></form>
style_footer();
return;
}
if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
}
|
| ︙ | ︙ | |||
798 799 800 801 802 803 804 |
zCurrent = db_get(zName, 0);
db_multi_exec("%s", zCurrent);
}
}
style_header("Skins");
@ <p>A "skin" is a combination of
| | | | | | | | | 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 |
zCurrent = db_get(zName, 0);
db_multi_exec("%s", zCurrent);
}
}
style_header("Skins");
@ <p>A "skin" is a combination of
@ <a href="setup_editcss">CSS</a>,
@ <a href="setup_header">Header</a>,
@ <a href="setup_footer">Footer</a>, and
@ <a href="setup_logo">Logo</a> that determines the look and feel
@ of the web interface.</p>
@
@ <h2>Available Skins:</h2>
@ <ol>
for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
z = aBuiltinSkin[i].zName;
if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
@ <li><p>%h(z). <b>Currently In Use</b></p>
}else{
@ <li><form action="%s(g.zBaseURL)/setup_skin" method="post"><div>
@ %h(z).
@ <input type="hidden" name="sn" value="%h(z)" />
@ <input type="submit" name="load" value="Use This Skin" />
@ </div></form></li>
}
}
db_prepare(&q,
"SELECT substr(name, 6), value FROM config"
" WHERE name GLOB 'skin:*'"
" ORDER BY name"
);
while( db_step(&q)==SQLITE_ROW ){
const char *zN = db_column_text(&q, 0);
const char *zV = db_column_text(&q, 1);
if( strcmp(zV, zCurrent)==0 ){
@ <li><p>%h(zN). <b>Currently In Use</b></p>
}else{
@ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
@ %h(zN).
@ <input type="hidden" name="sn" value="%h(zN)">
@ <input type="submit" name="load" value="Use This Skin">
@ <input type="submit" name="del1" value="Delete This Skin">
@ </form></li>
}
}
db_finalize(&q);
@ </ol>
style_footer();
db_end_transaction(0);
}
|
Changes to src/sqlite3.c.
1 2 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite | | | 1 2 3 4 5 6 7 8 9 10 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite ** version 3.7.2. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a one translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements ** of 5% are more are commonly seen when SQLite is compiled as a single ** translation unit. ** ** This file is all you need to compile SQLite. To use SQLite in other |
| ︙ | ︙ | |||
211 212 213 214 215 216 217 | /* ** The maximum value of a ?nnn wildcard that the parser will accept. */ #ifndef SQLITE_MAX_VARIABLE_NUMBER # define SQLITE_MAX_VARIABLE_NUMBER 999 #endif | | | < > > | | | | | < | | > | 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 | /* ** The maximum value of a ?nnn wildcard that the parser will accept. */ #ifndef SQLITE_MAX_VARIABLE_NUMBER # define SQLITE_MAX_VARIABLE_NUMBER 999 #endif /* Maximum page size. The upper bound on this value is 65536. This a limit ** imposed by the use of 16-bit offsets within each page. ** ** Earlier versions of SQLite allowed the user to change this value at ** compile time. This is no longer permitted, on the grounds that it creates ** a library that is technically incompatible with an SQLite library ** compiled with a different limit. If a process operating on a database ** with a page-size of 65536 bytes crashes, then an instance of SQLite ** compiled with the default page-size limit will not be able to rollback ** the aborted transaction. This could lead to database corruption. */ #ifdef SQLITE_MAX_PAGE_SIZE # undef SQLITE_MAX_PAGE_SIZE #endif #define SQLITE_MAX_PAGE_SIZE 65536 /* ** The default size of a database page. */ #ifndef SQLITE_DEFAULT_PAGE_SIZE # define SQLITE_DEFAULT_PAGE_SIZE 1024 |
| ︙ | ︙ | |||
629 630 631 632 633 634 635 | ** The SQLITE_VERSION_NUMBER for any given release of SQLite will also ** be larger than the release from which it is derived. Either Y will ** be held constant and Z will be incremented or else Y will be incremented ** and Z will be reset to zero. ** ** Since version 3.6.18, SQLite source code has been stored in the ** <a href="http://www.fossil-scm.org/">Fossil configuration management | | | | | | 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 | ** The SQLITE_VERSION_NUMBER for any given release of SQLite will also ** be larger than the release from which it is derived. Either Y will ** be held constant and Z will be incremented or else Y will be incremented ** and Z will be reset to zero. ** ** Since version 3.6.18, SQLite source code has been stored in the ** <a href="http://www.fossil-scm.org/">Fossil configuration management ** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to ** a string which identifies a particular check-in of SQLite ** within its configuration management system. ^The SQLITE_SOURCE_ID ** string contains the date and time of the check-in (UTC) and an SHA1 ** hash of the entire source tree. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.2" #define SQLITE_VERSION_NUMBER 3007002 #define SQLITE_SOURCE_ID "2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3" /* ** 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 |
| ︙ | ︙ | |||
686 687 688 689 690 691 692 | ** CAPI3REF: Run-Time Library Compilation Options Diagnostics ** ** ^The sqlite3_compileoption_used() function returns 0 or 1 ** indicating whether the specified option was defined at ** compile time. ^The SQLITE_ prefix may be omitted from the ** option name passed to sqlite3_compileoption_used(). ** | | | | 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 | ** CAPI3REF: Run-Time Library Compilation Options Diagnostics ** ** ^The sqlite3_compileoption_used() function returns 0 or 1 ** indicating whether the specified option was defined at ** compile time. ^The SQLITE_ prefix may be omitted from the ** option name passed to sqlite3_compileoption_used(). ** ** ^The sqlite3_compileoption_get() function allows iterating ** over the list of options that were defined at compile time by ** returning the N-th compile time option string. ^If N is out of range, ** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_ ** prefix is omitted from any strings returned by ** sqlite3_compileoption_get(). ** ** ^Support for the diagnostic functions sqlite3_compileoption_used() ** and sqlite3_compileoption_get() may be omitted by specifying the ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. ** ** See also: SQL functions [sqlite_compileoption_used()] and ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS SQLITE_API int sqlite3_compileoption_used(const char *zOptName); |
| ︙ | ︙ | |||
800 801 802 803 804 805 806 | #endif /* ** CAPI3REF: Closing A Database Connection ** ** ^The sqlite3_close() routine is the destructor for the [sqlite3] object. ** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is | | | 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 | #endif /* ** CAPI3REF: Closing A Database Connection ** ** ^The sqlite3_close() routine is the destructor for the [sqlite3] object. ** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is ** successfully destroyed and all associated resources are deallocated. ** ** Applications must [sqlite3_finalize | finalize] all [prepared statements] ** and [sqlite3_blob_close | close] all [BLOB handles] associated with ** the [sqlite3] object prior to attempting to close the object. ^If ** sqlite3_close() is called on a [database connection] that still has ** outstanding [prepared statements] or [BLOB handles], then it returns ** SQLITE_BUSY. |
| ︙ | ︙ | |||
1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 | ** ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the ** current transaction. This hint is not guaranteed to be accurate but it ** is often close. The underlying VFS might choose to preallocate database ** file space based on this hint in order to help writes to the database ** file run faster. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only | > > > > > > > > > | 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 | ** ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the ** current transaction. This hint is not guaranteed to be accurate but it ** is often close. The underlying VFS might choose to preallocate database ** file space based on this hint in order to help writes to the database ** file run faster. ** ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS ** extends and truncates the database file in chunks of a size specified ** by the user. The fourth argument to [sqlite3_file_control()] should ** point to an integer (type int) containing the new chunk-size to use ** for the nominated database. Allocating database file space in large ** chunks (say 1MB at a time), may reduce file-system fragmentation and ** improve performance on some systems. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only |
| ︙ | ︙ | |||
3195 3196 3197 3198 3199 3200 3201 | ** <li> ?NNN ** <li> :VVV ** <li> @VVV ** <li> $VVV ** </ul> ** ** In the templates above, NNN represents an integer literal, | | | 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 | ** <li> ?NNN ** <li> :VVV ** <li> @VVV ** <li> $VVV ** </ul> ** ** In the templates above, NNN represents an integer literal, ** and VVV represents an alphanumeric identifier.)^ ^The values of these ** parameters (also called "host parameter names" or "SQL parameters") ** can be set using the sqlite3_bind_*() routines defined here. ** ** ^The first argument to the sqlite3_bind_*() routines is always ** a pointer to the [sqlite3_stmt] object returned from ** [sqlite3_prepare_v2()] or its variants. ** |
| ︙ | ︙ | |||
3974 3975 3976 3977 3978 3979 3980 | SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context ** | | | 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 | SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context ** ** Implementations of aggregate SQL functions use this ** routine to allocate memory for storing their state. ** ** ^The first time the sqlite3_aggregate_context(C,N) routine is called ** for a particular aggregate function, SQLite ** allocates N of memory, zeroes out that memory, and returns a pointer ** to the new memory. ^On second and subsequent calls to ** sqlite3_aggregate_context() for the same aggregate function instance, |
| ︙ | ︙ | |||
4246 4247 4248 4249 4250 4251 4252 | ** expects pointers to be UTF-16 strings in the native byte order, or the ** argument can be [SQLITE_UTF16_ALIGNED] if the ** the routine expects pointers to 16-bit word aligned strings ** of UTF-16 in the native byte order. ** ** A pointer to the user supplied routine must be passed as the fifth ** argument. ^If it is NULL, this is the same as deleting the collation | | | 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 | ** expects pointers to be UTF-16 strings in the native byte order, or the ** argument can be [SQLITE_UTF16_ALIGNED] if the ** the routine expects pointers to 16-bit word aligned strings ** of UTF-16 in the native byte order. ** ** A pointer to the user supplied routine must be passed as the fifth ** argument. ^If it is NULL, this is the same as deleting the collation ** sequence (so that SQLite cannot call it any more). ** ^Each time the application supplied function is invoked, it is passed ** as its first parameter a copy of the void* passed as the fourth argument ** to sqlite3_create_collation() or sqlite3_create_collation16(). ** ** ^The remaining arguments to the application-supplied routine are two strings, ** each represented by a (length, data) pair and encoded in the encoding ** that was passed as the third argument when the collation sequence was |
| ︙ | ︙ | |||
5464 5465 5466 5467 5468 5469 5470 | ** of a valid mutex handle. The implementations of the methods defined ** by this structure are not required to handle this case, the results ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). ** ** The xMutexInit() method must be threadsafe. ^It must be harmless to | | | 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 | ** of a valid mutex handle. The implementations of the methods defined ** by this structure are not required to handle this case, the results ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). ** ** The xMutexInit() method must be threadsafe. ^It must be harmless to ** invoke xMutexInit() multiple times within the same process and without ** intervening calls to xMutexEnd(). Second and subsequent calls to ** xMutexInit() must be no-ops. ** ** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] ** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory ** allocation for a static mutex. ^However xMutexAlloc() may use SQLite ** memory allocation for a fast or recursive mutex. |
| ︙ | ︙ | |||
5634 5635 5636 5637 5638 5639 5640 | #define SQLITE_TESTCTRL_PGHDRSZ 17 #define SQLITE_TESTCTRL_LAST 17 /* ** CAPI3REF: SQLite Runtime Status ** ** ^This interface is used to retrieve runtime status information | | | 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 | #define SQLITE_TESTCTRL_PGHDRSZ 17 #define SQLITE_TESTCTRL_LAST 17 /* ** CAPI3REF: SQLite Runtime Status ** ** ^This interface is used to retrieve runtime status information ** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes ** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^ ** ^The current value of the parameter is returned into *pCurrent. ** ^The highest recorded value is returned in *pHighwater. ^If the ** resetFlag is true, then the highest record value is reset after ** *pHighwater is written. ^(Some parameters do not record the highest |
| ︙ | ︙ | |||
5760 5761 5762 5763 5764 5765 5766 | ** CAPI3REF: Database Connection Status ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the ** database connection object to be interrogated. ^The second argument ** is an integer constant, taken from the set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that | | | 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 | ** CAPI3REF: Database Connection Status ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the ** database connection object to be interrogated. ^The second argument ** is an integer constant, taken from the set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that ** determines the parameter to interrogate. The set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely ** to grow in future releases of SQLite. ** ** ^The current value of the requested parameter is written into *pCur ** and the highest instantaneous value is written into *pHiwtr. ^If ** the resetFlg is true, then the highest instantaneous value is ** reset back down to the current value. |
| ︙ | ︙ | |||
6182 6183 6184 6185 6186 6187 6188 | ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** ** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b> ** ** ^Each call to sqlite3_backup_step() sets two values inside ** the [sqlite3_backup] object: the number of pages still to be backed | | | 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 | ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** ** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b> ** ** ^Each call to sqlite3_backup_step() sets two values inside ** the [sqlite3_backup] object: the number of pages still to be backed ** up and the total number of pages in the source database file. ** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces ** retrieve these two values, respectively. ** ** ^The values returned by these functions are only updated by ** sqlite3_backup_step(). ^If the source database is modified during a backup ** operation, then the values are not updated to account for any extra ** pages that need to be updated or the size of the source database file |
| ︙ | ︙ | |||
6278 6279 6280 6281 6282 6283 6284 | ** the other connections to use as the blocking connection. ** ** ^(There may be at most one unlock-notify callback registered by a ** blocked connection. If sqlite3_unlock_notify() is called when the ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing | | | 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 | ** the other connections to use as the blocking connection. ** ** ^(There may be at most one unlock-notify callback registered by a ** blocked connection. If sqlite3_unlock_notify() is called when the ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing ** unlock-notify callback is canceled. ^The blocked connections ** unlock-notify callback may also be canceled by closing the blocked ** connection using [sqlite3_close()]. ** ** The unlock-notify callback is not reentrant. If an application invokes ** any sqlite3_xxx API functions from within an unlock-notify callback, a ** crash or deadlock may be the result. ** |
| ︙ | ︙ | |||
6360 6361 6362 6363 6364 6365 6366 | /* ** CAPI3REF: String Comparison ** ** ^The [sqlite3_strnicmp()] API allows applications and extensions to ** compare the contents of two buffers containing UTF-8 strings in a | | | 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 | /* ** CAPI3REF: String Comparison ** ** ^The [sqlite3_strnicmp()] API allows applications and extensions to ** compare the contents of two buffers containing UTF-8 strings in a ** case-independent fashion, using the same definition of case independence ** that SQLite uses internally when comparing identifiers. */ SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); /* ** CAPI3REF: Error Logging Interface ** |
| ︙ | ︙ | |||
7866 7867 7868 7869 7870 7871 7872 | void(*)(DbPage*) ); SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); | | | 7876 7877 7878 7879 7880 7881 7882 7883 7884 7885 7886 7887 7888 7889 7890 | void(*)(DbPage*) ); SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager); 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 int sqlite3PagerMaxPageCount(Pager*, int); SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int); SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int); SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*); SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*); |
| ︙ | ︙ | |||
7893 7894 7895 7896 7897 7898 7899 | 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. */ | | | 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 | 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 *zMaster, int); SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*); SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); |
| ︙ | ︙ | |||
9181 9182 9183 9184 9185 9186 9187 |
/*
** An instance of the following structure is passed as the first
** argument to sqlite3VdbeKeyCompare and is used to control the
** comparison of the two index keys.
*/
struct KeyInfo {
sqlite3 *db; /* The database connection */
| | | | 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 |
/*
** An instance of the following structure is passed as the first
** argument to sqlite3VdbeKeyCompare and is used to control the
** comparison of the two index keys.
*/
struct KeyInfo {
sqlite3 *db; /* The database connection */
u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
u16 nField; /* Number of entries in aColl[] */
u8 *aSortOrder; /* Sort order for each column. May be NULL */
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
};
/*
** An instance of the following structure holds information about a
** single index record that has already been parsed out into individual
** values.
|
| ︙ | ︙ | |||
14329 14330 14331 14332 14333 14334 14335 |
sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods);
}
/*
** Set the "type" of an allocation.
*/
SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){
| | | | 14339 14340 14341 14342 14343 14344 14345 14346 14347 14348 14349 14350 14351 14352 14353 14354 14355 14356 14357 14358 14359 14360 14361 14362 14363 14364 14365 14366 14367 14368 14369 14370 14371 14372 |
sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods);
}
/*
** Set the "type" of an allocation.
*/
SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){
if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
struct MemBlockHdr *pHdr;
pHdr = sqlite3MemsysGetHeader(p);
assert( pHdr->iForeGuard==FOREGUARD );
pHdr->eType = eType;
}
}
/*
** Return TRUE if the mask of type in eType matches the type of the
** allocation p. Also return true if p==NULL.
**
** This routine is designed for use within an assert() statement, to
** verify the type of an allocation. For example:
**
** assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
*/
SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){
int rc = 1;
if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
struct MemBlockHdr *pHdr;
pHdr = sqlite3MemsysGetHeader(p);
assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
if( (pHdr->eType&eType)==0 ){
rc = 0;
}
}
|
| ︙ | ︙ | |||
14370 14371 14372 14373 14374 14375 14376 |
** This routine is designed for use within an assert() statement, to
** verify the type of an allocation. For example:
**
** assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
*/
SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){
int rc = 1;
| | | 14380 14381 14382 14383 14384 14385 14386 14387 14388 14389 14390 14391 14392 14393 14394 |
** This routine is designed for use within an assert() statement, to
** verify the type of an allocation. For example:
**
** assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
*/
SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){
int rc = 1;
if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
struct MemBlockHdr *pHdr;
pHdr = sqlite3MemsysGetHeader(p);
assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
if( (pHdr->eType&eType)!=0 ){
rc = 0;
}
}
|
| ︙ | ︙ | |||
16622 16623 16624 16625 16626 16627 16628 16629 16630 16631 16632 16633 16634 16635 |
}
}
#else
/* Use the built-in recursive mutexes if they are available.
*/
pthread_mutex_lock(&p->mutex);
#if SQLITE_MUTEX_NREF
p->owner = pthread_self();
p->nRef++;
#endif
#endif
#ifdef SQLITE_DEBUG
if( p->trace ){
| > | 16632 16633 16634 16635 16636 16637 16638 16639 16640 16641 16642 16643 16644 16645 16646 |
}
}
#else
/* Use the built-in recursive mutexes if they are available.
*/
pthread_mutex_lock(&p->mutex);
#if SQLITE_MUTEX_NREF
assert( p->nRef>0 || p->owner==0 );
p->owner = pthread_self();
p->nRef++;
#endif
#endif
#ifdef SQLITE_DEBUG
if( p->trace ){
|
| ︙ | ︙ | |||
16694 16695 16696 16697 16698 16699 16700 16701 16702 16703 16704 16705 16706 16707 |
** is undefined if the mutex is not currently entered or
** is not currently allocated. SQLite will never do either.
*/
static void pthreadMutexLeave(sqlite3_mutex *p){
assert( pthreadMutexHeld(p) );
#if SQLITE_MUTEX_NREF
p->nRef--;
#endif
assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
if( p->nRef==0 ){
pthread_mutex_unlock(&p->mutex);
}
| > | 16705 16706 16707 16708 16709 16710 16711 16712 16713 16714 16715 16716 16717 16718 16719 |
** is undefined if the mutex is not currently entered or
** is not currently allocated. SQLite will never do either.
*/
static void pthreadMutexLeave(sqlite3_mutex *p){
assert( pthreadMutexHeld(p) );
#if SQLITE_MUTEX_NREF
p->nRef--;
if( p->nRef==0 ) p->owner = 0;
#endif
assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
if( p->nRef==0 ){
pthread_mutex_unlock(&p->mutex);
}
|
| ︙ | ︙ | |||
16958 16959 16960 16961 16962 16963 16964 |
/*
** This routine deallocates a previously
** allocated mutex. SQLite is careful to deallocate every
** mutex that it allocates.
*/
static void winMutexFree(sqlite3_mutex *p){
assert( p );
| | | 16970 16971 16972 16973 16974 16975 16976 16977 16978 16979 16980 16981 16982 16983 16984 |
/*
** This routine deallocates a previously
** allocated mutex. SQLite is careful to deallocate every
** mutex that it allocates.
*/
static void winMutexFree(sqlite3_mutex *p){
assert( p );
assert( p->nRef==0 && p->owner==0 );
assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
DeleteCriticalSection(&p->mutex);
sqlite3_free(p);
}
/*
** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
|
| ︙ | ︙ | |||
16982 16983 16984 16985 16986 16987 16988 16989 16990 16991 16992 16993 16994 16995 |
static void winMutexEnter(sqlite3_mutex *p){
#ifdef SQLITE_DEBUG
DWORD tid = GetCurrentThreadId();
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
#endif
EnterCriticalSection(&p->mutex);
#ifdef SQLITE_DEBUG
p->owner = tid;
p->nRef++;
if( p->trace ){
printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
}
#endif
}
| > | 16994 16995 16996 16997 16998 16999 17000 17001 17002 17003 17004 17005 17006 17007 17008 |
static void winMutexEnter(sqlite3_mutex *p){
#ifdef SQLITE_DEBUG
DWORD tid = GetCurrentThreadId();
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
#endif
EnterCriticalSection(&p->mutex);
#ifdef SQLITE_DEBUG
assert( p->nRef>0 || p->owner==0 );
p->owner = tid;
p->nRef++;
if( p->trace ){
printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
}
#endif
}
|
| ︙ | ︙ | |||
17035 17036 17037 17038 17039 17040 17041 17042 17043 17044 17045 17046 17047 17048 |
*/
static void winMutexLeave(sqlite3_mutex *p){
#ifndef NDEBUG
DWORD tid = GetCurrentThreadId();
assert( p->nRef>0 );
assert( p->owner==tid );
p->nRef--;
assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
#endif
LeaveCriticalSection(&p->mutex);
#ifdef SQLITE_DEBUG
if( p->trace ){
printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
}
| > | 17048 17049 17050 17051 17052 17053 17054 17055 17056 17057 17058 17059 17060 17061 17062 |
*/
static void winMutexLeave(sqlite3_mutex *p){
#ifndef NDEBUG
DWORD tid = GetCurrentThreadId();
assert( p->nRef>0 );
assert( p->owner==tid );
p->nRef--;
if( p->nRef==0 ) p->owner = 0;
assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
#endif
LeaveCriticalSection(&p->mutex);
#ifdef SQLITE_DEBUG
if( p->trace ){
printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
}
|
| ︙ | ︙ | |||
22607 22608 22609 22610 22611 22612 22613 22614 22615 22616 22617 22618 22619 22620 | unsigned char eFileLock; /* The type of lock held on this fd */ int lastErrno; /* The unix errno from last I/O error */ void *lockingContext; /* Locking style specific state */ UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ int fileFlags; /* Miscellanous flags */ const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) unsigned fsFlags; /* cached details from statfs() */ #endif #if OS_VXWORKS | > | 22621 22622 22623 22624 22625 22626 22627 22628 22629 22630 22631 22632 22633 22634 22635 | unsigned char eFileLock; /* The type of lock held on this fd */ int lastErrno; /* The unix errno from last I/O error */ void *lockingContext; /* Locking style specific state */ UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ int fileFlags; /* Miscellanous flags */ const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) unsigned fsFlags; /* cached details from statfs() */ #endif #if OS_VXWORKS |
| ︙ | ︙ | |||
25365 25366 25367 25368 25369 25370 25371 25372 25373 25374 25375 25376 25377 25378 25379 25380 25381 25382 25383 25384 25385 25386 25387 |
while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){
amt -= wrote;
offset += wrote;
pBuf = &((char*)pBuf)[wrote];
}
SimulateIOError(( wrote=(-1), amt=1 ));
SimulateDiskfullError(( wrote=0, amt=1 ));
if( amt>0 ){
if( wrote<0 ){
/* lastErrno set by seekAndWrite */
return SQLITE_IOERR_WRITE;
}else{
pFile->lastErrno = 0; /* not a system error */
return SQLITE_FULL;
}
}
return SQLITE_OK;
}
#ifdef SQLITE_TEST
/*
** Count the number of fullsyncs and normal syncs. This is used to test
** that syncs and fullsyncs are occurring at the right times.
| > > | 25380 25381 25382 25383 25384 25385 25386 25387 25388 25389 25390 25391 25392 25393 25394 25395 25396 25397 25398 25399 25400 25401 25402 25403 25404 |
while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){
amt -= wrote;
offset += wrote;
pBuf = &((char*)pBuf)[wrote];
}
SimulateIOError(( wrote=(-1), amt=1 ));
SimulateDiskfullError(( wrote=0, amt=1 ));
if( amt>0 ){
if( wrote<0 ){
/* lastErrno set by seekAndWrite */
return SQLITE_IOERR_WRITE;
}else{
pFile->lastErrno = 0; /* not a system error */
return SQLITE_FULL;
}
}
return SQLITE_OK;
}
#ifdef SQLITE_TEST
/*
** Count the number of fullsyncs and normal syncs. This is used to test
** that syncs and fullsyncs are occurring at the right times.
|
| ︙ | ︙ | |||
25575 25576 25577 25578 25579 25580 25581 25582 |
return rc;
}
/*
** Truncate an open file to a specified size
*/
static int unixTruncate(sqlite3_file *id, i64 nByte){
int rc;
| > | > > > > > > > > > > | | | | | 25592 25593 25594 25595 25596 25597 25598 25599 25600 25601 25602 25603 25604 25605 25606 25607 25608 25609 25610 25611 25612 25613 25614 25615 25616 25617 25618 25619 25620 25621 25622 25623 25624 25625 25626 25627 25628 25629 25630 25631 25632 25633 25634 |
return rc;
}
/*
** Truncate an open file to a specified size
*/
static int unixTruncate(sqlite3_file *id, i64 nByte){
unixFile *pFile = (unixFile *)id;
int rc;
assert( pFile );
SimulateIOError( return SQLITE_IOERR_TRUNCATE );
/* If the user has configured a chunk-size for this file, truncate the
** file so that it consists of an integer number of chunks (i.e. the
** actual file size after the operation may be larger than the requested
** size).
*/
if( pFile->szChunk ){
nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
}
rc = ftruncate(pFile->h, (off_t)nByte);
if( rc ){
pFile->lastErrno = errno;
return SQLITE_IOERR_TRUNCATE;
}else{
#ifndef NDEBUG
/* If we are doing a normal write to a database file (as opposed to
** doing a hot-journal rollback or a write to some file other than a
** normal database file) and we truncate the file to zero length,
** that effectively updates the change counter. This might happen
** when restoring a database using the backup API from a zero-length
** source.
*/
if( pFile->inNormalWrite && nByte==0 ){
pFile->transCntrChng = 1;
}
#endif
return SQLITE_OK;
}
}
|
| ︙ | ︙ | |||
25635 25636 25637 25638 25639 25640 25641 25642 25643 25644 25645 25646 25647 25648 25649 25650 25651 25652 25653 25654 25655 |
/*
** Handler for proxy-locking file-control verbs. Defined below in the
** proxying locking division.
*/
static int proxyFileControl(sqlite3_file*,int,void*);
#endif
/*
** Information and control of an open file handle.
*/
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
switch( op ){
case SQLITE_FCNTL_LOCKSTATE: {
*(int*)pArg = ((unixFile*)id)->eFileLock;
return SQLITE_OK;
}
case SQLITE_LAST_ERRNO: {
*(int*)pArg = ((unixFile*)id)->lastErrno;
return SQLITE_OK;
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < | < < > > > | 25663 25664 25665 25666 25667 25668 25669 25670 25671 25672 25673 25674 25675 25676 25677 25678 25679 25680 25681 25682 25683 25684 25685 25686 25687 25688 25689 25690 25691 25692 25693 25694 25695 25696 25697 25698 25699 25700 25701 25702 25703 25704 25705 25706 25707 25708 25709 25710 25711 25712 25713 25714 25715 25716 25717 25718 25719 25720 25721 25722 25723 25724 25725 25726 25727 25728 25729 25730 25731 25732 25733 25734 25735 25736 25737 25738 25739 25740 25741 25742 25743 25744 |
/*
** Handler for proxy-locking file-control verbs. Defined below in the
** proxying locking division.
*/
static int proxyFileControl(sqlite3_file*,int,void*);
#endif
/*
** This function is called to handle the SQLITE_FCNTL_SIZE_HINT
** file-control operation.
**
** If the user has configured a chunk-size for this file, it could be
** that the file needs to be extended at this point. Otherwise, the
** SQLITE_FCNTL_SIZE_HINT operation is a no-op for Unix.
*/
static int fcntlSizeHint(unixFile *pFile, i64 nByte){
if( pFile->szChunk ){
i64 nSize; /* Required file size */
struct stat buf; /* Used to hold return values of fstat() */
if( fstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT;
nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk;
if( nSize>(i64)buf.st_size ){
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
if( posix_fallocate(pFile->h, buf.st_size, nSize-buf.st_size) ){
return SQLITE_IOERR_WRITE;
}
#else
/* If the OS does not have posix_fallocate(), fake it. First use
** ftruncate() to set the file size, then write a single byte to
** the last byte in each block within the extended region. This
** is the same technique used by glibc to implement posix_fallocate()
** on systems that do not have a real fallocate() system call.
*/
int nBlk = buf.st_blksize; /* File-system block size */
i64 iWrite; /* Next offset to write to */
int nWrite; /* Return value from seekAndWrite() */
if( ftruncate(pFile->h, nSize) ){
pFile->lastErrno = errno;
return SQLITE_IOERR_TRUNCATE;
}
iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1;
do {
nWrite = seekAndWrite(pFile, iWrite, "", 1);
iWrite += nBlk;
} while( nWrite==1 && iWrite<nSize );
if( nWrite!=1 ) return SQLITE_IOERR_WRITE;
#endif
}
}
return SQLITE_OK;
}
/*
** Information and control of an open file handle.
*/
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
switch( op ){
case SQLITE_FCNTL_LOCKSTATE: {
*(int*)pArg = ((unixFile*)id)->eFileLock;
return SQLITE_OK;
}
case SQLITE_LAST_ERRNO: {
*(int*)pArg = ((unixFile*)id)->lastErrno;
return SQLITE_OK;
}
case SQLITE_FCNTL_CHUNK_SIZE: {
((unixFile*)id)->szChunk = *(int *)pArg;
return SQLITE_OK;
}
case SQLITE_FCNTL_SIZE_HINT: {
return fcntlSizeHint((unixFile *)id, *(i64 *)pArg);
}
#ifndef NDEBUG
/* The pager calls this method to signal that it has done
** a rollback and that the database is therefore unchanged and
** it hence it is OK for the transaction change counter to be
** unchanged.
*/
|
| ︙ | ︙ | |||
28520 28521 28522 28523 28524 28525 28526 |
pCtx->pOldMethod = pFile->pMethod;
pFile->pMethod = &proxyIoMethods;
}else{
if( pCtx->conchFile ){
pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile);
sqlite3_free(pCtx->conchFile);
}
| | | 28595 28596 28597 28598 28599 28600 28601 28602 28603 28604 28605 28606 28607 28608 28609 |
pCtx->pOldMethod = pFile->pMethod;
pFile->pMethod = &proxyIoMethods;
}else{
if( pCtx->conchFile ){
pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile);
sqlite3_free(pCtx->conchFile);
}
sqlite3DbFree(0, pCtx->lockProxyPath);
sqlite3_free(pCtx->conchFilePath);
sqlite3_free(pCtx);
}
OSTRACE(("TRANSPROXY %d %s\n", pFile->h,
(rc==SQLITE_OK ? "ok" : "failed")));
return rc;
}
|
| ︙ | ︙ | |||
28711 28712 28713 28714 28715 28716 28717 |
rc = proxyReleaseConch(pFile);
if( rc ) return rc;
}
rc = conchFile->pMethod->xClose((sqlite3_file*)conchFile);
if( rc ) return rc;
sqlite3_free(conchFile);
}
| | | | 28786 28787 28788 28789 28790 28791 28792 28793 28794 28795 28796 28797 28798 28799 28800 28801 28802 |
rc = proxyReleaseConch(pFile);
if( rc ) return rc;
}
rc = conchFile->pMethod->xClose((sqlite3_file*)conchFile);
if( rc ) return rc;
sqlite3_free(conchFile);
}
sqlite3DbFree(0, pCtx->lockProxyPath);
sqlite3_free(pCtx->conchFilePath);
sqlite3DbFree(0, pCtx->dbPath);
/* restore the original locking context and pMethod then close it */
pFile->lockingContext = pCtx->oldLockingContext;
pFile->pMethod = pCtx->pOldMethod;
sqlite3_free(pCtx);
return pFile->pMethod->xClose(id);
}
return SQLITE_OK;
|
| ︙ | ︙ | |||
29159 29160 29161 29162 29163 29164 29165 29166 29167 29168 29169 29170 29171 29172 | HANDLE h; /* Handle for accessing the file */ unsigned char locktype; /* Type of lock currently held on this file */ short sharedLockByte; /* Randomly chosen byte used as a shared lock */ DWORD lastErrno; /* The Windows errno from the last I/O error */ DWORD sectorSize; /* Sector size of the device file is on */ winShm *pShm; /* Instance of shared memory on this file */ const char *zPath; /* Full pathname of this file */ #if SQLITE_OS_WINCE WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ HANDLE hMutex; /* Mutex used to control access to shared lock */ HANDLE hShared; /* Shared memory segment used for locking */ winceLock local; /* Locks obtained by this instance of winFile */ winceLock *shared; /* Global shared lock memory for the file */ #endif | > | 29234 29235 29236 29237 29238 29239 29240 29241 29242 29243 29244 29245 29246 29247 29248 | HANDLE h; /* Handle for accessing the file */ unsigned char locktype; /* Type of lock currently held on this file */ short sharedLockByte; /* Randomly chosen byte used as a shared lock */ DWORD lastErrno; /* The Windows errno from the last I/O error */ DWORD sectorSize; /* Sector size of the device file is on */ winShm *pShm; /* Instance of shared memory on this file */ const char *zPath; /* Full pathname of this file */ int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */ #if SQLITE_OS_WINCE WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ HANDLE hMutex; /* Mutex used to control access to shared lock */ HANDLE hShared; /* Shared memory segment used for locking */ winceLock local; /* Locks obtained by this instance of winFile */ winceLock *shared; /* Global shared lock memory for the file */ #endif |
| ︙ | ︙ | |||
29669 29670 29671 29672 29673 29674 29675 29676 29677 29678 29679 29680 29681 29682 | *****************************************************************************/ #endif /* SQLITE_OS_WINCE */ /***************************************************************************** ** The next group of routines implement the I/O methods specified ** by the sqlite3_io_methods object. ******************************************************************************/ /* ** Close a file. ** ** It is reported that an attempt to close a handle might sometimes ** fail. This is a very unreasonable result, but windows is notorious ** for being unreasonable so I do not doubt that it might happen. If | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 29745 29746 29747 29748 29749 29750 29751 29752 29753 29754 29755 29756 29757 29758 29759 29760 29761 29762 29763 29764 29765 29766 29767 29768 29769 29770 29771 29772 29773 29774 29775 29776 29777 29778 29779 29780 29781 29782 29783 29784 29785 29786 29787 29788 29789 29790 29791 29792 29793 29794 |
*****************************************************************************/
#endif /* SQLITE_OS_WINCE */
/*****************************************************************************
** The next group of routines implement the I/O methods specified
** by the sqlite3_io_methods object.
******************************************************************************/
/*
** Some microsoft compilers lack this definition.
*/
#ifndef INVALID_SET_FILE_POINTER
# define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif
/*
** Move the current position of the file handle passed as the first
** argument to offset iOffset within the file. If successful, return 0.
** Otherwise, set pFile->lastErrno and return non-zero.
*/
static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
LONG upperBits; /* Most sig. 32 bits of new offset */
LONG lowerBits; /* Least sig. 32 bits of new offset */
DWORD dwRet; /* Value returned by SetFilePointer() */
upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
lowerBits = (LONG)(iOffset & 0xffffffff);
/* API oddity: If successful, SetFilePointer() returns a dword
** containing the lower 32-bits of the new file-offset. Or, if it fails,
** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
** whether an error has actually occured, it is also necessary to call
** GetLastError().
*/
dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){
pFile->lastErrno = GetLastError();
return 1;
}
return 0;
}
/*
** Close a file.
**
** It is reported that an attempt to close a handle might sometimes
** fail. This is a very unreasonable result, but windows is notorious
** for being unreasonable so I do not doubt that it might happen. If
|
| ︙ | ︙ | |||
29712 29713 29714 29715 29716 29717 29718 |
}
#endif
OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed"));
OpenCounter(-1);
return rc ? SQLITE_OK : SQLITE_IOERR;
}
| < < < < < < < < < < | < | | < | | | < < | > > | | | | < < | | < < | > > < < < < < < < | > > > > > | < < < | | < | < | | | > > > > > < < > < < < | < | | > | > > > > > > > > | > | < | > | | 29824 29825 29826 29827 29828 29829 29830 29831 29832 29833 29834 29835 29836 29837 29838 29839 29840 29841 29842 29843 29844 29845 29846 29847 29848 29849 29850 29851 29852 29853 29854 29855 29856 29857 29858 29859 29860 29861 29862 29863 29864 29865 29866 29867 29868 29869 29870 29871 29872 29873 29874 29875 29876 29877 29878 29879 29880 29881 29882 29883 29884 29885 29886 29887 29888 29889 29890 29891 29892 29893 29894 29895 29896 29897 29898 29899 29900 29901 29902 29903 29904 29905 29906 29907 29908 29909 29910 29911 29912 29913 29914 29915 29916 29917 29918 29919 29920 29921 29922 29923 29924 29925 29926 29927 29928 29929 29930 29931 29932 29933 29934 29935 29936 29937 29938 29939 29940 29941 29942 29943 29944 29945 29946 |
}
#endif
OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed"));
OpenCounter(-1);
return rc ? SQLITE_OK : SQLITE_IOERR;
}
/*
** Read data from a file into a buffer. Return SQLITE_OK if all
** bytes were read successfully and SQLITE_IOERR if anything goes
** wrong.
*/
static int winRead(
sqlite3_file *id, /* File to read from */
void *pBuf, /* Write content into this buffer */
int amt, /* Number of bytes to read */
sqlite3_int64 offset /* Begin reading at this offset */
){
winFile *pFile = (winFile*)id; /* file handle */
DWORD nRead; /* Number of bytes actually read from file */
assert( id!=0 );
SimulateIOError(return SQLITE_IOERR_READ);
OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype));
if( seekWinFile(pFile, offset) ){
return SQLITE_FULL;
}
if( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
pFile->lastErrno = GetLastError();
return SQLITE_IOERR_READ;
}
if( nRead<(DWORD)amt ){
/* Unread parts of the buffer must be zero-filled */
memset(&((char*)pBuf)[nRead], 0, amt-nRead);
return SQLITE_IOERR_SHORT_READ;
}
return SQLITE_OK;
}
/*
** Write data from a buffer into a file. Return SQLITE_OK on success
** or some other error code on failure.
*/
static int winWrite(
sqlite3_file *id, /* File to write into */
const void *pBuf, /* The bytes to be written */
int amt, /* Number of bytes to write */
sqlite3_int64 offset /* Offset into the file to begin writing at */
){
int rc; /* True if error has occured, else false */
winFile *pFile = (winFile*)id; /* File handle */
assert( amt>0 );
assert( pFile );
SimulateIOError(return SQLITE_IOERR_WRITE);
SimulateDiskfullError(return SQLITE_FULL);
OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype));
rc = seekWinFile(pFile, offset);
if( rc==0 ){
u8 *aRem = (u8 *)pBuf; /* Data yet to be written */
int nRem = amt; /* Number of bytes yet to be written */
DWORD nWrite; /* Bytes written by each WriteFile() call */
while( nRem>0 && WriteFile(pFile->h, aRem, nRem, &nWrite, 0) && nWrite>0 ){
aRem += nWrite;
nRem -= nWrite;
}
if( nRem>0 ){
pFile->lastErrno = GetLastError();
rc = 1;
}
}
if( rc ){
if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){
return SQLITE_FULL;
}
return SQLITE_IOERR_WRITE;
}
return SQLITE_OK;
}
/*
** Truncate an open file to a specified size
*/
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
winFile *pFile = (winFile*)id; /* File handle object */
int rc = SQLITE_OK; /* Return code for this function */
assert( pFile );
OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte));
SimulateIOError(return SQLITE_IOERR_TRUNCATE);
/* If the user has configured a chunk-size for this file, truncate the
** file so that it consists of an integer number of chunks (i.e. the
** actual file size after the operation may be larger than the requested
** size).
*/
if( pFile->szChunk ){
nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
}
/* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
if( seekWinFile(pFile, nByte) ){
rc = SQLITE_IOERR_TRUNCATE;
}else if( 0==SetEndOfFile(pFile->h) ){
pFile->lastErrno = GetLastError();
rc = SQLITE_IOERR_TRUNCATE;
}
OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok"));
return rc;
}
#ifdef SQLITE_TEST
/*
** Count the number of fullsyncs and normal syncs. This is used to test
** that syncs and fullsyncs are occuring at the right times.
|
| ︙ | ︙ | |||
30195 30196 30197 30198 30199 30200 30201 30202 30203 30204 30205 30206 30207 30208 |
case SQLITE_FCNTL_LOCKSTATE: {
*(int*)pArg = ((winFile*)id)->locktype;
return SQLITE_OK;
}
case SQLITE_LAST_ERRNO: {
*(int*)pArg = (int)((winFile*)id)->lastErrno;
return SQLITE_OK;
}
case SQLITE_FCNTL_SIZE_HINT: {
sqlite3_int64 sz = *(sqlite3_int64*)pArg;
SimulateIOErrorBenign(1);
winTruncate(id, sz);
SimulateIOErrorBenign(0);
return SQLITE_OK;
| > > > > | 30296 30297 30298 30299 30300 30301 30302 30303 30304 30305 30306 30307 30308 30309 30310 30311 30312 30313 |
case SQLITE_FCNTL_LOCKSTATE: {
*(int*)pArg = ((winFile*)id)->locktype;
return SQLITE_OK;
}
case SQLITE_LAST_ERRNO: {
*(int*)pArg = (int)((winFile*)id)->lastErrno;
return SQLITE_OK;
}
case SQLITE_FCNTL_CHUNK_SIZE: {
((winFile*)id)->szChunk = *(int *)pArg;
return SQLITE_OK;
}
case SQLITE_FCNTL_SIZE_HINT: {
sqlite3_int64 sz = *(sqlite3_int64*)pArg;
SimulateIOErrorBenign(1);
winTruncate(id, sz);
SimulateIOErrorBenign(0);
return SQLITE_OK;
|
| ︙ | ︙ | |||
31747 31748 31749 31750 31751 31752 31753 | ** sometimes grow into tens of thousands or larger. The size of the ** Bitvec object is the number of pages in the database file at the ** start of a transaction, and is thus usually less than a few thousand, ** but can be as large as 2 billion for a really big database. */ /* Size of the Bitvec structure in bytes. */ | | | 31852 31853 31854 31855 31856 31857 31858 31859 31860 31861 31862 31863 31864 31865 31866 | ** sometimes grow into tens of thousands or larger. The size of the ** Bitvec object is the number of pages in the database file at the ** start of a transaction, and is thus usually less than a few thousand, ** but can be as large as 2 billion for a really big database. */ /* Size of the Bitvec structure in bytes. */ #define BITVEC_SZ 512 /* Round the union size down to the nearest pointer boundary, since that's how ** it will be aligned within the Bitvec struct. */ #define BITVEC_USIZE (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*)) /* Type of the array "element" for the bitmap representation. ** Should be a power of 2, and ideally, evenly divide into BITVEC_USIZE. |
| ︙ | ︙ | |||
32905 32906 32907 32908 32909 32910 32911 32912 32913 32914 32915 32916 32917 32918 |
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
iSize = sqlite3MallocSize(p);
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
sqlite3_free(p);
}
}
/*
** Allocate a new page object initially associated with cache pCache.
*/
static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
int nByte = sizeof(PgHdr1) + pCache->szPage;
void *pPg = pcache1Alloc(nByte);
PgHdr1 *p;
| > > > > > > > > > > > > > > > > > > > | 33010 33011 33012 33013 33014 33015 33016 33017 33018 33019 33020 33021 33022 33023 33024 33025 33026 33027 33028 33029 33030 33031 33032 33033 33034 33035 33036 33037 33038 33039 33040 33041 33042 |
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
iSize = sqlite3MallocSize(p);
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
sqlite3_free(p);
}
}
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
/*
** Return the size of a pache allocation
*/
static int pcache1MemSize(void *p){
assert( sqlite3_mutex_held(pcache1.mutex) );
if( p>=pcache1.pStart && p<pcache1.pEnd ){
return pcache1.szSlot;
}else{
int iSize;
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
iSize = sqlite3MallocSize(p);
sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
return iSize;
}
}
#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
/*
** Allocate a new page object initially associated with cache pCache.
*/
static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
int nByte = sizeof(PgHdr1) + pCache->szPage;
void *pPg = pcache1Alloc(nByte);
PgHdr1 *p;
|
| ︙ | ︙ | |||
33453 33454 33455 33456 33457 33458 33459 |
*/
SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){
int nFree = 0;
if( pcache1.pStart==0 ){
PgHdr1 *p;
pcache1EnterMutex();
while( (nReq<0 || nFree<nReq) && (p=pcache1.pLruTail) ){
| | | 33577 33578 33579 33580 33581 33582 33583 33584 33585 33586 33587 33588 33589 33590 33591 |
*/
SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){
int nFree = 0;
if( pcache1.pStart==0 ){
PgHdr1 *p;
pcache1EnterMutex();
while( (nReq<0 || nFree<nReq) && (p=pcache1.pLruTail) ){
nFree += pcache1MemSize(PGHDR1_TO_PAGE(p));
pcache1PinPage(p);
pcache1RemoveFromHash(p);
pcache1FreePage(p);
}
pcache1LeaveMutex();
}
return nFree;
|
| ︙ | ︙ | |||
33962 33963 33964 33965 33966 33967 33968 | #ifdef SQLITE_OMIT_WAL # define sqlite3WalOpen(x,y,z) 0 # define sqlite3WalClose(w,x,y,z) 0 # define sqlite3WalBeginReadTransaction(y,z) 0 # define sqlite3WalEndReadTransaction(z) # define sqlite3WalRead(v,w,x,y,z) 0 | | | 34086 34087 34088 34089 34090 34091 34092 34093 34094 34095 34096 34097 34098 34099 34100 | #ifdef SQLITE_OMIT_WAL # define sqlite3WalOpen(x,y,z) 0 # define sqlite3WalClose(w,x,y,z) 0 # define sqlite3WalBeginReadTransaction(y,z) 0 # define sqlite3WalEndReadTransaction(z) # define sqlite3WalRead(v,w,x,y,z) 0 # define sqlite3WalDbsize(y) 0 # define sqlite3WalBeginWriteTransaction(y) 0 # define sqlite3WalEndWriteTransaction(x) 0 # define sqlite3WalUndo(x,y,z) 0 # define sqlite3WalSavepoint(y,z) # define sqlite3WalSavepointUndo(y,z) 0 # define sqlite3WalFrames(u,v,w,x,y,z) 0 # define sqlite3WalCheckpoint(u,v,w,x) 0 |
| ︙ | ︙ | |||
33998 33999 34000 34001 34002 34003 34004 | */ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *); SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal); /* Read a page from the write-ahead log, if it is present. */ SQLITE_PRIVATE int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut); | | < | | 34122 34123 34124 34125 34126 34127 34128 34129 34130 34131 34132 34133 34134 34135 34136 34137 | */ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *); SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal); /* Read a page from the write-ahead log, if it is present. */ SQLITE_PRIVATE int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut); /* If the WAL is not empty, return the size of the database. */ SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal); /* Obtain or release the WRITER lock. */ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal); SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal); /* Undo any frames written (but not committed) to the log */ SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx); |
| ︙ | ︙ | |||
34046 34047 34048 34049 34050 34051 34052 | #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* _WAL_H_ */ /************** End of wal.h *************************************************/ /************** Continuing where we left off in pager.c **********************/ | | | > > > > | 34169 34170 34171 34172 34173 34174 34175 34176 34177 34178 34179 34180 34181 34182 34183 34184 34185 34186 34187 34188 | #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* _WAL_H_ */ /************** End of wal.h *************************************************/ /************** Continuing where we left off in pager.c **********************/ /******************* NOTES ON THE DESIGN OF THE PAGER ************************ ** ** This comment block describes invariants that hold when using a rollback ** journal. These invariants do not apply for journal_mode=WAL, ** journal_mode=MEMORY, or journal_mode=OFF. ** ** Within this comment block, a page is deemed to have been synced ** automatically as soon as it is written when PRAGMA synchronous=OFF. ** Otherwise, the page is not synced until the xSync method of the VFS ** is called successfully on the file containing the page. ** ** Definition: A page of the database file is said to be "overwriteable" if |
| ︙ | ︙ | |||
34081 34082 34083 34084 34085 34086 34087 | ** ** (2) The content of a page written into the rollback journal exactly matches ** both the content in the database when the rollback journal was written ** and the content in the database at the beginning of the current ** transaction. ** ** (3) Writes to the database file are an integer multiple of the page size | | | 34208 34209 34210 34211 34212 34213 34214 34215 34216 34217 34218 34219 34220 34221 34222 | ** ** (2) The content of a page written into the rollback journal exactly matches ** both the content in the database when the rollback journal was written ** and the content in the database at the beginning of the current ** transaction. ** ** (3) Writes to the database file are an integer multiple of the page size ** in length and are aligned on a page boundary. ** ** (4) Reads from the database file are either aligned on a page boundary and ** an integer multiple of the page size in length or are taken from the ** first 100 bytes of the database file. ** ** (5) All writes to the database file are synced prior to the rollback journal ** being deleted, truncated, or zeroed. |
| ︙ | ︙ | |||
34112 34113 34114 34115 34116 34117 34118 | ** is called to restore the database file to the same size it was at ** the beginning of the transaction. (In some VFSes, the xTruncate ** method is a no-op, but that does not change the fact the SQLite will ** invoke it.) ** ** (9) Whenever the database file is modified, at least one bit in the range ** of bytes from 24 through 39 inclusive will be changed prior to releasing | | > | > | 34239 34240 34241 34242 34243 34244 34245 34246 34247 34248 34249 34250 34251 34252 34253 34254 34255 34256 34257 34258 34259 34260 34261 34262 34263 34264 34265 34266 34267 34268 | ** is called to restore the database file to the same size it was at ** the beginning of the transaction. (In some VFSes, the xTruncate ** method is a no-op, but that does not change the fact the SQLite will ** invoke it.) ** ** (9) Whenever the database file is modified, at least one bit in the range ** of bytes from 24 through 39 inclusive will be changed prior to releasing ** the EXCLUSIVE lock, thus signaling other connections on the same ** database to flush their caches. ** ** (10) The pattern of bits in bytes 24 through 39 shall not repeat in less ** than one billion transactions. ** ** (11) A database file is well-formed at the beginning and at the conclusion ** of every transaction. ** ** (12) An EXCLUSIVE lock is held on the database file when writing to ** the database file. ** ** (13) A SHARED lock is held on the database file while reading any ** content out of the database file. ** ******************************************************************************/ /* ** Macros for troubleshooting. Normally turned off */ #if 0 int sqlite3PagerTrace=1; /* True to enable tracing */ #define sqlite3DebugPrintf printf |
| ︙ | ︙ | |||
34150 34151 34152 34153 34154 34155 34156 | ** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file ** struct as its argument. */ #define PAGERID(p) ((int)(p->fd)) #define FILEHANDLEID(fd) ((int)fd) /* | > | | < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > | > > | | > > > > > > > > > > > > > > > > > | > > > | > > > | > > > | > > | > > > > > > > > > > | > > > > | > > > > > > | > > > > | > | > > | > > > > > > > > | | > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | < > > > > > > | > | < | < | > > > > | > > > > > > > > > > > > > > | > > > > > | > > > > > | > > > > > > > > > > > | > > > > > > > | | < < < < | 34279 34280 34281 34282 34283 34284 34285 34286 34287 34288 34289 34290 34291 34292 34293 34294 34295 34296 34297 34298 34299 34300 34301 34302 34303 34304 34305 34306 34307 34308 34309 34310 34311 34312 34313 34314 34315 34316 34317 34318 34319 34320 34321 34322 34323 34324 34325 34326 34327 34328 34329 34330 34331 34332 34333 34334 34335 34336 34337 34338 34339 34340 34341 34342 34343 34344 34345 34346 34347 34348 34349 34350 34351 34352 34353 34354 34355 34356 34357 34358 34359 34360 34361 34362 34363 34364 34365 34366 34367 34368 34369 34370 34371 34372 34373 34374 34375 34376 34377 34378 34379 34380 34381 34382 34383 34384 34385 34386 34387 34388 34389 34390 34391 34392 34393 34394 34395 34396 34397 34398 34399 34400 34401 34402 34403 34404 34405 34406 34407 34408 34409 34410 34411 34412 34413 34414 34415 34416 34417 34418 34419 34420 34421 34422 34423 34424 34425 34426 34427 34428 34429 34430 34431 34432 34433 34434 34435 34436 34437 34438 34439 34440 34441 34442 34443 34444 34445 34446 34447 34448 34449 34450 34451 34452 34453 34454 34455 34456 34457 34458 34459 34460 34461 34462 34463 34464 34465 34466 34467 34468 34469 34470 34471 34472 34473 34474 34475 34476 34477 34478 34479 34480 34481 34482 34483 34484 34485 34486 34487 34488 34489 34490 34491 34492 34493 34494 34495 34496 34497 34498 34499 34500 34501 34502 34503 34504 34505 34506 34507 34508 34509 34510 34511 34512 34513 34514 34515 34516 34517 34518 34519 34520 34521 34522 34523 34524 34525 34526 34527 34528 34529 34530 34531 34532 34533 34534 34535 34536 34537 34538 34539 34540 34541 34542 34543 34544 34545 34546 34547 34548 34549 34550 34551 34552 34553 34554 34555 34556 34557 34558 34559 34560 34561 34562 34563 34564 34565 |
** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file
** struct as its argument.
*/
#define PAGERID(p) ((int)(p->fd))
#define FILEHANDLEID(fd) ((int)fd)
/*
** The Pager.eState variable stores the current 'state' of a pager. A
** pager may be in any one of the seven states shown in the following
** state diagram.
**
** OPEN <------+------+
** | | |
** V | |
** +---------> READER-------+ |
** | | |
** | V |
** |<-------WRITER_LOCKED------> ERROR
** | | ^
** | V |
** |<------WRITER_CACHEMOD-------->|
** | | |
** | V |
** |<-------WRITER_DBMOD---------->|
** | | |
** | V |
** +<------WRITER_FINISHED-------->+
**
**
** List of state transitions and the C [function] that performs each:
**
** OPEN -> READER [sqlite3PagerSharedLock]
** READER -> OPEN [pager_unlock]
**
** READER -> WRITER_LOCKED [sqlite3PagerBegin]
** WRITER_LOCKED -> WRITER_CACHEMOD [pager_open_journal]
** WRITER_CACHEMOD -> WRITER_DBMOD [syncJournal]
** WRITER_DBMOD -> WRITER_FINISHED [sqlite3PagerCommitPhaseOne]
** WRITER_*** -> READER [pager_end_transaction]
**
** WRITER_*** -> ERROR [pager_error]
** ERROR -> OPEN [pager_unlock]
**
**
** OPEN:
**
** The pager starts up in this state. Nothing is guaranteed in this
** state - the file may or may not be locked and the database size is
** unknown. The database may not be read or written.
**
** * No read or write transaction is active.
** * Any lock, or no lock at all, may be held on the database file.
** * The dbSize, dbOrigSize and dbFileSize variables may not be trusted.
**
** READER:
**
** In this state all the requirements for reading the database in
** rollback (non-WAL) mode are met. Unless the pager is (or recently
** was) in exclusive-locking mode, a user-level read transaction is
** open. The database size is known in this state.
**
** A connection running with locking_mode=normal enters this state when
** it opens a read-transaction on the database and returns to state
** OPEN after the read-transaction is completed. However a connection
** running in locking_mode=exclusive (including temp databases) remains in
** this state even after the read-transaction is closed. The only way
** a locking_mode=exclusive connection can transition from READER to OPEN
** is via the ERROR state (see below).
**
** * A read transaction may be active (but a write-transaction cannot).
** * A SHARED or greater lock is held on the database file.
** * The dbSize variable may be trusted (even if a user-level read
** transaction is not active). The dbOrigSize and dbFileSize variables
** may not be trusted at this point.
** * If the database is a WAL database, then the WAL connection is open.
** * Even if a read-transaction is not open, it is guaranteed that
** there is no hot-journal in the file-system.
**
** WRITER_LOCKED:
**
** The pager moves to this state from READER when a write-transaction
** is first opened on the database. In WRITER_LOCKED state, all locks
** required to start a write-transaction are held, but no actual
** modifications to the cache or database have taken place.
**
** In rollback mode, a RESERVED or (if the transaction was opened with
** BEGIN EXCLUSIVE) EXCLUSIVE lock is obtained on the database file when
** moving to this state, but the journal file is not written to or opened
** to in this state. If the transaction is committed or rolled back while
** in WRITER_LOCKED state, all that is required is to unlock the database
** file.
**
** IN WAL mode, WalBeginWriteTransaction() is called to lock the log file.
** If the connection is running with locking_mode=exclusive, an attempt
** is made to obtain an EXCLUSIVE lock on the database file.
**
** * A write transaction is active.
** * If the connection is open in rollback-mode, a RESERVED or greater
** lock is held on the database file.
** * If the connection is open in WAL-mode, a WAL write transaction
** is open (i.e. sqlite3WalBeginWriteTransaction() has been successfully
** called).
** * The dbSize, dbOrigSize and dbFileSize variables are all valid.
** * The contents of the pager cache have not been modified.
** * The journal file may or may not be open.
** * Nothing (not even the first header) has been written to the journal.
**
** WRITER_CACHEMOD:
**
** A pager moves from WRITER_LOCKED state to this state when a page is
** first modified by the upper layer. In rollback mode the journal file
** is opened (if it is not already open) and a header written to the
** start of it. The database file on disk has not been modified.
**
** * A write transaction is active.
** * A RESERVED or greater lock is held on the database file.
** * The journal file is open and the first header has been written
** to it, but the header has not been synced to disk.
** * The contents of the page cache have been modified.
**
** WRITER_DBMOD:
**
** The pager transitions from WRITER_CACHEMOD into WRITER_DBMOD state
** when it modifies the contents of the database file. WAL connections
** never enter this state (since they do not modify the database file,
** just the log file).
**
** * A write transaction is active.
** * An EXCLUSIVE or greater lock is held on the database file.
** * The journal file is open and the first header has been written
** and synced to disk.
** * The contents of the page cache have been modified (and possibly
** written to disk).
**
** WRITER_FINISHED:
**
** It is not possible for a WAL connection to enter this state.
**
** A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD
** state after the entire transaction has been successfully written into the
** database file. In this state the transaction may be committed simply
** by finalizing the journal file. Once in WRITER_FINISHED state, it is
** not possible to modify the database further. At this point, the upper
** layer must either commit or rollback the transaction.
**
** * A write transaction is active.
** * An EXCLUSIVE or greater lock is held on the database file.
** * All writing and syncing of journal and database data has finished.
** If no error occured, all that remains is to finalize the journal to
** commit the transaction. If an error did occur, the caller will need
** to rollback the transaction.
**
** ERROR:
**
** The ERROR state is entered when an IO or disk-full error (including
** SQLITE_IOERR_NOMEM) occurs at a point in the code that makes it
** difficult to be sure that the in-memory pager state (cache contents,
** db size etc.) are consistent with the contents of the file-system.
**
** Temporary pager files may enter the ERROR state, but in-memory pagers
** cannot.
**
** For example, if an IO error occurs while performing a rollback,
** the contents of the page-cache may be left in an inconsistent state.
** At this point it would be dangerous to change back to READER state
** (as usually happens after a rollback). Any subsequent readers might
** report database corruption (due to the inconsistent cache), and if
** they upgrade to writers, they may inadvertently corrupt the database
** file. To avoid this hazard, the pager switches into the ERROR state
** instead of READER following such an error.
**
** Once it has entered the ERROR state, any attempt to use the pager
** to read or write data returns an error. Eventually, once all
** outstanding transactions have been abandoned, the pager is able to
** transition back to OPEN state, discarding the contents of the
** page-cache and any other in-memory state at the same time. Everything
** is reloaded from disk (and, if necessary, hot-journal rollback peformed)
** when a read-transaction is next opened on the pager (transitioning
** the pager into READER state). At that point the system has recovered
** from the error.
**
** Specifically, the pager jumps into the ERROR state if:
**
** 1. An error occurs while attempting a rollback. This happens in
** function sqlite3PagerRollback().
**
** 2. An error occurs while attempting to finalize a journal file
** following a commit in function sqlite3PagerCommitPhaseTwo().
**
** 3. An error occurs while attempting to write to the journal or
** database file in function pagerStress() in order to free up
** memory.
**
** In other cases, the error is returned to the b-tree layer. The b-tree
** layer then attempts a rollback operation. If the error condition
** persists, the pager enters the ERROR state via condition (1) above.
**
** Condition (3) is necessary because it can be triggered by a read-only
** statement executed within a transaction. In this case, if the error
** code were simply returned to the user, the b-tree layer would not
** automatically attempt a rollback, as it assumes that an error in a
** read-only statement cannot leave the pager in an internally inconsistent
** state.
**
** * The Pager.errCode variable is set to something other than SQLITE_OK.
** * There are one or more outstanding references to pages (after the
** last reference is dropped the pager should move back to OPEN state).
** * The pager is not an in-memory pager.
**
**
** Notes:
**
** * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the
** connection is open in WAL mode. A WAL connection is always in one
** of the first four states.
**
** * Normally, a connection open in exclusive mode is never in PAGER_OPEN
** state. There are two exceptions: immediately after exclusive-mode has
** been turned on (and before any read or write transactions are
** executed), and when the pager is leaving the "error state".
**
** * See also: assert_pager_state().
*/
#define PAGER_OPEN 0
#define PAGER_READER 1
#define PAGER_WRITER_LOCKED 2
#define PAGER_WRITER_CACHEMOD 3
#define PAGER_WRITER_DBMOD 4
#define PAGER_WRITER_FINISHED 5
#define PAGER_ERROR 6
/*
** The Pager.eLock variable is almost always set to one of the
** following locking-states, according to the lock currently held on
** the database file: NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK.
** This variable is kept up to date as locks are taken and released by
** the pagerLockDb() and pagerUnlockDb() wrappers.
**
** If the VFS xLock() or xUnlock() returns an error other than SQLITE_BUSY
** (i.e. one of the SQLITE_IOERR subtypes), it is not clear whether or not
** the operation was successful. In these circumstances pagerLockDb() and
** pagerUnlockDb() take a conservative approach - eLock is always updated
** when unlocking the file, and only updated when locking the file if the
** VFS call is successful. This way, the Pager.eLock variable may be set
** to a less exclusive (lower) value than the lock that is actually held
** at the system level, but it is never set to a more exclusive value.
**
** This is usually safe. If an xUnlock fails or appears to fail, there may
** be a few redundant xLock() calls or a lock may be held for longer than
** required, but nothing really goes wrong.
**
** The exception is when the database file is unlocked as the pager moves
** from ERROR to OPEN state. At this point there may be a hot-journal file
** in the file-system that needs to be rolled back (as part of a OPEN->SHARED
** transition, by the same pager or any other). If the call to xUnlock()
** fails at this point and the pager is left holding an EXCLUSIVE lock, this
** can confuse the call to xCheckReservedLock() call made later as part
** of hot-journal detection.
**
** xCheckReservedLock() is defined as returning true "if there is a RESERVED
** lock held by this process or any others". So xCheckReservedLock may
** return true because the caller itself is holding an EXCLUSIVE lock (but
** doesn't know it because of a previous error in xUnlock). If this happens
** a hot-journal may be mistaken for a journal being created by an active
** transaction in another process, causing SQLite to read from the database
** without rolling it back.
**
** To work around this, if a call to xUnlock() fails when unlocking the
** database in the ERROR state, Pager.eLock is set to UNKNOWN_LOCK. It
** is only changed back to a real locking state after a successful call
** to xLock(EXCLUSIVE). Also, the code to do the OPEN->SHARED state transition
** omits the check for a hot-journal if Pager.eLock is set to UNKNOWN_LOCK
** lock. Instead, it assumes a hot-journal exists and obtains an EXCLUSIVE
** lock on the database file before attempting to roll it back. See function
** PagerSharedLock() for more detail.
**
** Pager.eLock may only be set to UNKNOWN_LOCK when the pager is in
** PAGER_OPEN state.
*/
#define UNKNOWN_LOCK (EXCLUSIVE_LOCK+1)
/*
** A macro used for invoking the codec if there is one
*/
#ifdef SQLITE_HAS_CODEC
# define CODEC1(P,D,N,X,E) \
if( P->xCodec && P->xCodec(P->pCodec,D,N,X)==0 ){ E; }
|
| ︙ | ︙ | |||
34251 34252 34253 34254 34255 34256 34257 | Pgno iSubRec; /* Index of first record in sub-journal */ #ifndef SQLITE_OMIT_WAL u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */ #endif }; /* | | > | < | < < | < < | > > > | | | > | < < < < < < < > > | < < < < < < < < < < < < < < < < < < | | | | | | < < < < | | > > > > | | | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | < | | | | | < < | | < | < < > | | > > > > < | 34601 34602 34603 34604 34605 34606 34607 34608 34609 34610 34611 34612 34613 34614 34615 34616 34617 34618 34619 34620 34621 34622 34623 34624 34625 34626 34627 34628 34629 34630 34631 34632 34633 34634 34635 34636 34637 34638 34639 34640 34641 34642 34643 34644 34645 34646 34647 34648 34649 34650 34651 34652 34653 34654 34655 34656 34657 34658 34659 34660 34661 34662 34663 34664 34665 34666 34667 34668 34669 34670 34671 34672 34673 34674 34675 34676 34677 34678 34679 34680 34681 34682 34683 34684 34685 34686 34687 34688 34689 34690 34691 34692 34693 34694 34695 34696 34697 34698 34699 34700 34701 34702 34703 34704 34705 34706 34707 34708 34709 34710 34711 34712 34713 34714 34715 34716 34717 34718 34719 34720 34721 34722 34723 34724 34725 34726 34727 34728 34729 34730 34731 34732 34733 34734 34735 34736 34737 34738 34739 34740 34741 34742 34743 34744 34745 34746 34747 34748 34749 34750 34751 34752 34753 34754 34755 34756 34757 34758 34759 34760 34761 34762 34763 34764 34765 34766 34767 34768 34769 34770 34771 34772 34773 34774 34775 34776 34777 34778 34779 34780 34781 34782 34783 34784 34785 34786 34787 34788 34789 34790 34791 34792 34793 34794 34795 34796 34797 34798 34799 34800 34801 34802 34803 34804 34805 34806 34807 34808 34809 34810 34811 34812 34813 34814 34815 34816 34817 34818 34819 34820 34821 34822 34823 34824 34825 34826 34827 34828 34829 34830 34831 34832 34833 34834 34835 34836 34837 34838 34839 34840 34841 34842 |
Pgno iSubRec; /* Index of first record in sub-journal */
#ifndef SQLITE_OMIT_WAL
u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */
#endif
};
/*
** A open page cache is an instance of struct Pager. A description of
** some of the more important member variables follows:
**
** eState
**
** The current 'state' of the pager object. See the comment and state
** diagram above for a description of the pager state.
**
** eLock
**
** For a real on-disk database, the current lock held on the database file -
** NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK.
**
** For a temporary or in-memory database (neither of which require any
** locks), this variable is always set to EXCLUSIVE_LOCK. Since such
** databases always have Pager.exclusiveMode==1, this tricks the pager
** logic into thinking that it already has all the locks it will ever
** need (and no reason to release them).
**
** In some (obscure) circumstances, this variable may also be set to
** UNKNOWN_LOCK. See the comment above the #define of UNKNOWN_LOCK for
** details.
**
** changeCountDone
**
** This boolean variable is used to make sure that the change-counter
** (the 4-byte header field at byte offset 24 of the database file) is
** not updated more often than necessary.
**
** It is set to true when the change-counter field is updated, which
** can only happen if an exclusive lock is held on the database file.
** It is cleared (set to false) whenever an exclusive lock is
** relinquished on the database file. Each time a transaction is committed,
** The changeCountDone flag is inspected. If it is true, the work of
** updating the change-counter is omitted for the current transaction.
**
** This mechanism means that when running in exclusive mode, a connection
** need only update the change-counter once, for the first transaction
** committed.
**
** setMaster
**
** When PagerCommitPhaseOne() is called to commit a transaction, it may
** (or may not) specify a master-journal name to be written into the
** journal file before it is synced to disk.
**
** Whether or not a journal file contains a master-journal pointer affects
** the way in which the journal file is finalized after the transaction is
** committed or rolled back when running in "journal_mode=PERSIST" mode.
** If a journal file does not contain a master-journal pointer, it is
** finalized by overwriting the first journal header with zeroes. If
** it does contain a master-journal pointer the journal file is finalized
** by truncating it to zero bytes, just as if the connection were
** running in "journal_mode=truncate" mode.
**
** Journal files that contain master journal pointers cannot be finalized
** simply by overwriting the first journal-header with zeroes, as the
** master journal pointer could interfere with hot-journal rollback of any
** subsequently interrupted transaction that reuses the journal file.
**
** The flag is cleared as soon as the journal file is finalized (either
** by PagerCommitPhaseTwo or PagerRollback). If an IO error prevents the
** journal file from being successfully finalized, the setMaster flag
** is cleared anyway (and the pager will move to ERROR state).
**
** doNotSpill, doNotSyncSpill
**
** These two boolean variables control the behaviour of cache-spills
** (calls made by the pcache module to the pagerStress() routine to
** write cached data to the file-system in order to free up memory).
**
** When doNotSpill is non-zero, writing to the database from pagerStress()
** is disabled altogether. This is done in a very obscure case that
** comes up during savepoint rollback that requires the pcache module
** to allocate a new page to prevent the journal file from being written
** while it is being traversed by code in pager_playback().
**
** If doNotSyncSpill is non-zero, writing to the database from pagerStress()
** is permitted, but syncing the journal file is not. This flag is set
** by sqlite3PagerWrite() when the file-system sector-size is larger than
** the database page-size in order to prevent a journal sync from happening
** in between the journalling of two pages on the same sector.
**
** subjInMemory
**
** This is a boolean variable. If true, then any required sub-journal
** is opened as an in-memory journal file. If false, then in-memory
** sub-journals are only used for in-memory pager files.
**
** This variable is updated by the upper layer each time a new
** write-transaction is opened.
**
** dbSize, dbOrigSize, dbFileSize
**
** Variable dbSize is set to the number of pages in the database file.
** It is valid in PAGER_READER and higher states (all states except for
** OPEN and ERROR).
**
** dbSize is set based on the size of the database file, which may be
** larger than the size of the database (the value stored at offset
** 28 of the database header by the btree). If the size of the file
** is not an integer multiple of the page-size, the value stored in
** dbSize is rounded down (i.e. a 5KB file with 2K page-size has dbSize==2).
** Except, any file that is greater than 0 bytes in size is considered
** to have at least one page. (i.e. a 1KB file with 2K page-size leads
** to dbSize==1).
**
** During a write-transaction, if pages with page-numbers greater than
** dbSize are modified in the cache, dbSize is updated accordingly.
** Similarly, if the database is truncated using PagerTruncateImage(),
** dbSize is updated.
**
** Variables dbOrigSize and dbFileSize are valid in states
** PAGER_WRITER_LOCKED and higher. dbOrigSize is a copy of the dbSize
** variable at the start of the transaction. It is used during rollback,
** and to determine whether or not pages need to be journalled before
** being modified.
**
** Throughout a write-transaction, dbFileSize contains the size of
** the file on disk in pages. It is set to a copy of dbSize when the
** write-transaction is first opened, and updated when VFS calls are made
** to write or truncate the database file on disk.
**
** The only reason the dbFileSize variable is required is to suppress
** unnecessary calls to xTruncate() after committing a transaction. If,
** when a transaction is committed, the dbFileSize variable indicates
** that the database file is larger than the database image (Pager.dbSize),
** pager_truncate() is called. The pager_truncate() call uses xFilesize()
** to measure the database file on disk, and then truncates it if required.
** dbFileSize is not used when rolling back a transaction. In this case
** pager_truncate() is called unconditionally (which means there may be
** a call to xFilesize() that is not strictly required). In either case,
** pager_truncate() may cause the file to become smaller or larger.
**
** dbHintSize
**
** The dbHintSize variable is used to limit the number of calls made to
** the VFS xFileControl(FCNTL_SIZE_HINT) method.
**
** dbHintSize is set to a copy of the dbSize variable when a
** write-transaction is opened (at the same time as dbFileSize and
** dbOrigSize). If the xFileControl(FCNTL_SIZE_HINT) method is called,
** dbHintSize is increased to the number of pages that correspond to the
** size-hint passed to the method call. See pager_write_pagelist() for
** details.
**
** errCode
**
** The Pager.errCode variable is only ever used in PAGER_ERROR state. It
** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode
** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX
** sub-codes.
*/
struct Pager {
sqlite3_vfs *pVfs; /* OS functions to use for IO */
u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */
u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */
u8 useJournal; /* Use a rollback journal on this file */
u8 noReadlock; /* Do not bother to obtain readlocks */
u8 noSync; /* Do not sync the journal if true */
u8 fullSync; /* Do extra syncs of the journal for robustness */
u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */
u8 tempFile; /* zFilename is a temporary file */
u8 readOnly; /* True for a read-only database */
u8 memDb; /* True to inhibit all file I/O */
/**************************************************************************
** The following block contains those class members that change during
** routine opertion. Class members not in this block are either fixed
** when the pager is first created or else only change when there is a
** significant mode change (such as changing the page_size, locking_mode,
** or the journal_mode). From another view, these class members describe
** the "state" of the pager, while other class members describe the
** "configuration" of the pager.
*/
u8 eState; /* Pager state (OPEN, READER, WRITER_LOCKED..) */
u8 eLock; /* Current lock held on database file */
u8 changeCountDone; /* Set after incrementing the change-counter */
u8 setMaster; /* True if a m-j name has been written to jrnl */
u8 doNotSpill; /* Do not spill the cache when non-zero */
u8 doNotSyncSpill; /* Do not do a spill that requires jrnl sync */
u8 subjInMemory; /* True to use in-memory sub-journals */
Pgno dbSize; /* Number of pages in the database */
Pgno dbOrigSize; /* dbSize before the current transaction */
Pgno dbFileSize; /* Number of pages in the database file */
Pgno dbHintSize; /* Value passed to FCNTL_SIZE_HINT call */
int errCode; /* One of several kinds of errors */
int nRec; /* Pages journalled since last j-header written */
u32 cksumInit; /* Quasi-random value added to every checksum */
u32 nSubRec; /* Number of records written to sub-journal */
Bitvec *pInJournal; /* One bit for each page in the database file */
sqlite3_file *fd; /* File descriptor for database */
sqlite3_file *jfd; /* File descriptor for main journal */
sqlite3_file *sjfd; /* File descriptor for sub-journal */
i64 journalOff; /* Current write offset in the journal file */
i64 journalHdr; /* Byte offset to previous journal header */
sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */
PagerSavepoint *aSavepoint; /* Array of active savepoints */
int nSavepoint; /* Number of elements in aSavepoint[] */
char dbFileVers[16]; /* Changes whenever database file changes */
/*
** End of the routinely-changing class members
***************************************************************************/
u16 nExtra; /* Add this many bytes to each in-memory page */
i16 nReserve; /* Number of unused bytes at end of each page */
u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */
u32 sectorSize; /* Assumed sector size during rollback */
int pageSize; /* Number of bytes in a page */
Pgno mxPgno; /* Maximum allowed size of the database */
i64 journalSizeLimit; /* Size limit for persistent journal files */
char *zFilename; /* Name of the database file */
char *zJournal; /* Name of the journal file */
int (*xBusyHandler)(void*); /* Function to call when busy */
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
#ifdef SQLITE_TEST
int nHit, nMiss; /* Cache hits and missing */
int nRead, nWrite; /* Database pages read/written */
#endif
void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
#ifdef SQLITE_HAS_CODEC
void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */
void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */
void (*xCodecFree)(void*); /* Destructor for the codec */
void *pCodec; /* First argument to xCodec... methods */
#endif
char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */
PCache *pPCache; /* Pointer to page cache object */
#ifndef SQLITE_OMIT_WAL
Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */
char *zWal; /* File name for write-ahead log */
#endif
};
/*
|
| ︙ | ︙ | |||
34515 34516 34517 34518 34519 34520 34521 34522 34523 34524 34525 34526 34527 | #endif /* ** The maximum legal page number is (2^31 - 1). */ #define PAGER_MAX_PGNO 2147483647 #ifndef NDEBUG /* ** Usage: ** ** assert( assert_pager_state(pPager) ); */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 34907 34908 34909 34910 34911 34912 34913 34914 34915 34916 34917 34918 34919 34920 34921 34922 34923 34924 34925 34926 34927 34928 34929 34930 34931 34932 34933 34934 34935 34936 34937 34938 34939 34940 34941 34942 34943 34944 34945 34946 34947 34948 34949 34950 34951 34952 34953 34954 34955 34956 34957 34958 34959 34960 34961 34962 34963 34964 34965 34966 34967 34968 34969 34970 34971 34972 34973 34974 34975 34976 34977 34978 34979 34980 34981 34982 34983 34984 34985 34986 34987 34988 34989 34990 34991 34992 34993 34994 34995 34996 34997 34998 34999 35000 35001 35002 35003 35004 35005 35006 35007 35008 35009 35010 35011 35012 35013 35014 35015 35016 35017 35018 35019 35020 35021 35022 35023 35024 35025 35026 35027 35028 35029 35030 35031 35032 35033 35034 35035 35036 35037 35038 35039 35040 35041 35042 35043 35044 35045 35046 35047 35048 35049 35050 35051 35052 35053 35054 35055 35056 35057 35058 35059 35060 35061 35062 35063 35064 35065 35066 35067 35068 35069 35070 35071 35072 35073 35074 35075 35076 35077 35078 35079 35080 35081 35082 35083 35084 35085 35086 35087 35088 35089 35090 35091 35092 35093 35094 35095 35096 35097 35098 35099 35100 35101 35102 35103 35104 35105 35106 35107 35108 35109 35110 35111 35112 35113 35114 35115 35116 35117 35118 35119 35120 35121 35122 35123 35124 35125 35126 35127 35128 35129 35130 35131 35132 35133 35134 35135 |
#endif
/*
** The maximum legal page number is (2^31 - 1).
*/
#define PAGER_MAX_PGNO 2147483647
/*
** 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) ){ ...
**
** instead of
**
** if( pPager->jfd->pMethods ){ ...
*/
#define isOpen(pFd) ((pFd)->pMethods)
/*
** Return true if this pager uses a write-ahead log instead of the usual
** rollback journal. Otherwise false.
*/
#ifndef SQLITE_OMIT_WAL
static int pagerUseWal(Pager *pPager){
return (pPager->pWal!=0);
}
#else
# define pagerUseWal(x) 0
# define pagerRollbackWal(x) 0
# define pagerWalFrames(v,w,x,y,z) 0
# define pagerOpenWalIfPresent(z) SQLITE_OK
# define pagerBeginReadTransaction(z) SQLITE_OK
#endif
#ifndef NDEBUG
/*
** Usage:
**
** assert( assert_pager_state(pPager) );
**
** This function runs many asserts to try to find inconsistencies in
** the internal state of the Pager object.
*/
static int assert_pager_state(Pager *p){
Pager *pPager = p;
/* State must be valid. */
assert( p->eState==PAGER_OPEN
|| p->eState==PAGER_READER
|| p->eState==PAGER_WRITER_LOCKED
|| p->eState==PAGER_WRITER_CACHEMOD
|| p->eState==PAGER_WRITER_DBMOD
|| p->eState==PAGER_WRITER_FINISHED
|| p->eState==PAGER_ERROR
);
/* Regardless of the current state, a temp-file connection always behaves
** as if it has an exclusive lock on the database file. It never updates
** the change-counter field, so the changeCountDone flag is always set.
*/
assert( p->tempFile==0 || p->eLock==EXCLUSIVE_LOCK );
assert( p->tempFile==0 || pPager->changeCountDone );
/* If the useJournal flag is clear, the journal-mode must be "OFF".
** And if the journal-mode is "OFF", the journal file must not be open.
*/
assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal );
assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) );
/* Check that MEMDB implies noSync. And an in-memory journal. Since
** this means an in-memory pager performs no IO at all, it cannot encounter
** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing
** a journal file. (although the in-memory journal implementation may
** return SQLITE_IOERR_NOMEM while the journal file is being written). It
** is therefore not possible for an in-memory pager to enter the ERROR
** state.
*/
if( MEMDB ){
assert( p->noSync );
assert( p->journalMode==PAGER_JOURNALMODE_OFF
|| p->journalMode==PAGER_JOURNALMODE_MEMORY
);
assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN );
assert( pagerUseWal(p)==0 );
}
/* If changeCountDone is set, a RESERVED lock or greater must be held
** on the file.
*/
assert( pPager->changeCountDone==0 || pPager->eLock>=RESERVED_LOCK );
assert( p->eLock!=PENDING_LOCK );
switch( p->eState ){
case PAGER_OPEN:
assert( !MEMDB );
assert( pPager->errCode==SQLITE_OK );
assert( sqlite3PcacheRefCount(pPager->pPCache)==0 || pPager->tempFile );
break;
case PAGER_READER:
assert( pPager->errCode==SQLITE_OK );
assert( p->eLock!=UNKNOWN_LOCK );
assert( p->eLock>=SHARED_LOCK || p->noReadlock );
break;
case PAGER_WRITER_LOCKED:
assert( p->eLock!=UNKNOWN_LOCK );
assert( pPager->errCode==SQLITE_OK );
if( !pagerUseWal(pPager) ){
assert( p->eLock>=RESERVED_LOCK );
}
assert( pPager->dbSize==pPager->dbOrigSize );
assert( pPager->dbOrigSize==pPager->dbFileSize );
assert( pPager->dbOrigSize==pPager->dbHintSize );
assert( pPager->setMaster==0 );
break;
case PAGER_WRITER_CACHEMOD:
assert( p->eLock!=UNKNOWN_LOCK );
assert( pPager->errCode==SQLITE_OK );
if( !pagerUseWal(pPager) ){
/* It is possible that if journal_mode=wal here that neither the
** journal file nor the WAL file are open. This happens during
** a rollback transaction that switches from journal_mode=off
** to journal_mode=wal.
*/
assert( p->eLock>=RESERVED_LOCK );
assert( isOpen(p->jfd)
|| p->journalMode==PAGER_JOURNALMODE_OFF
|| p->journalMode==PAGER_JOURNALMODE_WAL
);
}
assert( pPager->dbOrigSize==pPager->dbFileSize );
assert( pPager->dbOrigSize==pPager->dbHintSize );
break;
case PAGER_WRITER_DBMOD:
assert( p->eLock==EXCLUSIVE_LOCK );
assert( pPager->errCode==SQLITE_OK );
assert( !pagerUseWal(pPager) );
assert( p->eLock>=EXCLUSIVE_LOCK );
assert( isOpen(p->jfd)
|| p->journalMode==PAGER_JOURNALMODE_OFF
|| p->journalMode==PAGER_JOURNALMODE_WAL
);
assert( pPager->dbOrigSize<=pPager->dbHintSize );
break;
case PAGER_WRITER_FINISHED:
assert( p->eLock==EXCLUSIVE_LOCK );
assert( pPager->errCode==SQLITE_OK );
assert( !pagerUseWal(pPager) );
assert( isOpen(p->jfd)
|| p->journalMode==PAGER_JOURNALMODE_OFF
|| p->journalMode==PAGER_JOURNALMODE_WAL
);
break;
case PAGER_ERROR:
/* There must be at least one outstanding reference to the pager if
** in ERROR state. Otherwise the pager should have already dropped
** back to OPEN state.
*/
assert( pPager->errCode!=SQLITE_OK );
assert( sqlite3PcacheRefCount(pPager->pPCache)>0 );
break;
}
return 1;
}
/*
** Return a pointer to a human readable string in a static buffer
** containing the state of the Pager object passed as an argument. This
** is intended to be used within debuggers. For example, as an alternative
** to "print *pPager" in gdb:
**
** (gdb) printf "%s", print_pager_state(pPager)
*/
static char *print_pager_state(Pager *p){
static char zRet[1024];
sqlite3_snprintf(1024, zRet,
"Filename: %s\n"
"State: %s errCode=%d\n"
"Lock: %s\n"
"Locking mode: locking_mode=%s\n"
"Journal mode: journal_mode=%s\n"
"Backing store: tempFile=%d memDb=%d useJournal=%d\n"
"Journal: journalOff=%lld journalHdr=%lld\n"
"Size: dbsize=%d dbOrigSize=%d dbFileSize=%d\n"
, p->zFilename
, p->eState==PAGER_OPEN ? "OPEN" :
p->eState==PAGER_READER ? "READER" :
p->eState==PAGER_WRITER_LOCKED ? "WRITER_LOCKED" :
p->eState==PAGER_WRITER_CACHEMOD ? "WRITER_CACHEMOD" :
p->eState==PAGER_WRITER_DBMOD ? "WRITER_DBMOD" :
p->eState==PAGER_WRITER_FINISHED ? "WRITER_FINISHED" :
p->eState==PAGER_ERROR ? "ERROR" : "?error?"
, (int)p->errCode
, p->eLock==NO_LOCK ? "NO_LOCK" :
p->eLock==RESERVED_LOCK ? "RESERVED" :
p->eLock==EXCLUSIVE_LOCK ? "EXCLUSIVE" :
p->eLock==SHARED_LOCK ? "SHARED" :
p->eLock==UNKNOWN_LOCK ? "UNKNOWN" : "?error?"
, p->exclusiveMode ? "exclusive" : "normal"
, p->journalMode==PAGER_JOURNALMODE_MEMORY ? "memory" :
p->journalMode==PAGER_JOURNALMODE_OFF ? "off" :
p->journalMode==PAGER_JOURNALMODE_DELETE ? "delete" :
p->journalMode==PAGER_JOURNALMODE_PERSIST ? "persist" :
p->journalMode==PAGER_JOURNALMODE_TRUNCATE ? "truncate" :
p->journalMode==PAGER_JOURNALMODE_WAL ? "wal" : "?error?"
, (int)p->tempFile, (int)p->memDb, (int)p->useJournal
, p->journalOff, p->journalHdr
, (int)p->dbSize, (int)p->dbOrigSize, (int)p->dbFileSize
);
return zRet;
}
#endif
/*
** Return true if it is necessary to write page *pPg into the sub-journal.
** A page needs to be written into the sub-journal if there exists one
** or more open savepoints for which:
**
|
| ︙ | ︙ | |||
34582 34583 34584 34585 34586 34587 34588 34589 34590 34591 34592 34593 34594 34595 34596 34597 34598 34599 34600 |
return rc;
}
/*
** Write a 32-bit integer into a string buffer in big-endian byte order.
*/
#define put32bits(A,B) sqlite3Put4byte((u8*)A,B)
/*
** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK
** on success or an error code is something goes wrong.
*/
static int write32bits(sqlite3_file *fd, i64 offset, u32 val){
char ac[4];
put32bits(ac, val);
return sqlite3OsWrite(fd, ac, 4, offset);
}
/*
| > | | | < | < > | < < > > | > > > > > > > > | > > > > > < > > > > > > > > | < | | > > > > > > > > | | 35173 35174 35175 35176 35177 35178 35179 35180 35181 35182 35183 35184 35185 35186 35187 35188 35189 35190 35191 35192 35193 35194 35195 35196 35197 35198 35199 35200 35201 35202 35203 35204 35205 35206 35207 35208 35209 35210 35211 35212 35213 35214 35215 35216 35217 35218 35219 35220 35221 35222 35223 35224 35225 35226 35227 35228 35229 35230 35231 35232 35233 35234 35235 35236 35237 35238 35239 35240 35241 35242 35243 35244 35245 35246 |
return rc;
}
/*
** Write a 32-bit integer into a string buffer in big-endian byte order.
*/
#define put32bits(A,B) sqlite3Put4byte((u8*)A,B)
/*
** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK
** on success or an error code is something goes wrong.
*/
static int write32bits(sqlite3_file *fd, i64 offset, u32 val){
char ac[4];
put32bits(ac, val);
return sqlite3OsWrite(fd, ac, 4, offset);
}
/*
** Unlock the database file to level eLock, which must be either NO_LOCK
** or SHARED_LOCK. Regardless of whether or not the call to xUnlock()
** succeeds, set the Pager.eLock variable to match the (attempted) new lock.
**
** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is
** called, do not modify it. See the comment above the #define of
** UNKNOWN_LOCK for an explanation of this.
*/
static int pagerUnlockDb(Pager *pPager, int eLock){
int rc = SQLITE_OK;
assert( !pPager->exclusiveMode );
assert( eLock==NO_LOCK || eLock==SHARED_LOCK );
assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
if( isOpen(pPager->fd) ){
assert( pPager->eLock>=eLock );
rc = sqlite3OsUnlock(pPager->fd, eLock);
if( pPager->eLock!=UNKNOWN_LOCK ){
pPager->eLock = (u8)eLock;
}
IOTRACE(("UNLOCK %p %d\n", pPager, eLock))
}
return rc;
}
/*
** Lock the database file to level eLock, which must be either SHARED_LOCK,
** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the
** Pager.eLock variable to the new locking state.
**
** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is
** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK.
** See the comment above the #define of UNKNOWN_LOCK for an explanation
** of this.
*/
static int pagerLockDb(Pager *pPager, int eLock){
int rc = SQLITE_OK;
assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK );
if( pPager->eLock<eLock || pPager->eLock==UNKNOWN_LOCK ){
rc = sqlite3OsLock(pPager->fd, eLock);
if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){
pPager->eLock = (u8)eLock;
IOTRACE(("LOCK %p %d\n", pPager, eLock))
}
}
return rc;
}
/*
** This function determines whether or not the atomic-write optimization
** can be used with this pager. The optimization can be used if:
**
** (a) the value returned by OsDeviceCharacteristics() indicates that
|
| ︙ | ︙ | |||
34690 34691 34692 34693 34694 34695 34696 |
** The CHECK_PAGE macro takes a PgHdr* as an argument. If SQLITE_CHECK_PAGES
** is defined, and NDEBUG is not defined, an assert() statement checks
** that the page is either dirty or still matches the calculated page-hash.
*/
#define CHECK_PAGE(x) checkPage(x)
static void checkPage(PgHdr *pPg){
Pager *pPager = pPg->pPager;
| | | > | 35308 35309 35310 35311 35312 35313 35314 35315 35316 35317 35318 35319 35320 35321 35322 35323 35324 35325 35326 35327 35328 35329 |
** The CHECK_PAGE macro takes a PgHdr* as an argument. If SQLITE_CHECK_PAGES
** is defined, and NDEBUG is not defined, an assert() statement checks
** that the page is either dirty or still matches the calculated page-hash.
*/
#define CHECK_PAGE(x) checkPage(x)
static void checkPage(PgHdr *pPg){
Pager *pPager = pPg->pPager;
assert( pPager->eState!=PAGER_ERROR );
assert( (pPg->flags&PGHDR_DIRTY) || pPg->pageHash==pager_pagehash(pPg) );
}
#else
#define pager_datahash(X,Y) 0
#define pager_pagehash(X) 0
#define pager_set_pagehash(X)
#define CHECK_PAGE(x)
#endif /* SQLITE_CHECK_PAGES */
/*
** When this is called the journal file for pager pPager must be open.
** This function attempts to read a master journal file name from the
** end of the file and, if successful, copies it into memory supplied
|
| ︙ | ︙ | |||
34863 34864 34865 34866 34867 34868 34869 |
** - 4 bytes: Database page size.
**
** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
*/
static int writeJournalHdr(Pager *pPager){
int rc = SQLITE_OK; /* Return code */
char *zHeader = pPager->pTmpSpace; /* Temporary space used to build header */
| | | 35482 35483 35484 35485 35486 35487 35488 35489 35490 35491 35492 35493 35494 35495 35496 |
** - 4 bytes: Database page size.
**
** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
*/
static int writeJournalHdr(Pager *pPager){
int rc = SQLITE_OK; /* Return code */
char *zHeader = pPager->pTmpSpace; /* Temporary space used to build header */
u32 nHeader = (u32)pPager->pageSize;/* Size of buffer pointed to by zHeader */
u32 nWrite; /* Bytes of header sector written */
int ii; /* Loop counter */
assert( isOpen(pPager->jfd) ); /* Journal file must be open. */
if( nHeader>JOURNAL_HDR_SZ(pPager) ){
nHeader = JOURNAL_HDR_SZ(pPager);
|
| ︙ | ︙ | |||
34906 34907 34908 34909 34910 34911 34912 | ** * When the pager is in no-sync mode. Corruption can follow a ** power failure in this case anyway. ** ** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees ** that garbage data is never appended to the journal file. */ assert( isOpen(pPager->fd) || pPager->noSync ); | | | 35525 35526 35527 35528 35529 35530 35531 35532 35533 35534 35535 35536 35537 35538 35539 |
** * When the pager is in no-sync mode. Corruption can follow a
** power failure in this case anyway.
**
** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees
** that garbage data is never appended to the journal file.
*/
assert( isOpen(pPager->fd) || pPager->noSync );
if( pPager->noSync || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY)
|| (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
){
memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff);
}else{
memset(zHeader, 0, sizeof(aJournalMagic)+4);
}
|
| ︙ | ︙ | |||
35030 35031 35032 35033 35034 35035 35036 |
){
return rc;
}
if( pPager->journalOff==0 ){
u32 iPageSize; /* Page-size field of journal header */
u32 iSectorSize; /* Sector-size field of journal header */
| < > > > > > > > > | 35649 35650 35651 35652 35653 35654 35655 35656 35657 35658 35659 35660 35661 35662 35663 35664 35665 35666 35667 35668 35669 35670 35671 35672 35673 35674 35675 35676 35677 |
){
return rc;
}
if( pPager->journalOff==0 ){
u32 iPageSize; /* Page-size field of journal header */
u32 iSectorSize; /* Sector-size field of journal header */
/* Read the page-size and sector-size journal header fields. */
if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+20, &iSectorSize))
|| SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+24, &iPageSize))
){
return rc;
}
/* Versions of SQLite prior to 3.5.8 set the page-size field of the
** journal header to zero. In this case, assume that the Pager.pageSize
** variable is already set to the correct page size.
*/
if( iPageSize==0 ){
iPageSize = pPager->pageSize;
}
/* Check that the values read from the page-size and sector-size fields
** are within range. To be 'in range', both values need to be a power
** of two greater than or equal to 512 or 32, and not greater than their
** respective compile time maximum limits.
*/
if( iPageSize<512 || iSectorSize<32
|
| ︙ | ︙ | |||
35060 35061 35062 35063 35064 35065 35066 |
return SQLITE_DONE;
}
/* Update the page-size to match the value read from the journal.
** Use a testcase() macro to make sure that malloc failure within
** PagerSetPagesize() is tested.
*/
| < | < | 35686 35687 35688 35689 35690 35691 35692 35693 35694 35695 35696 35697 35698 35699 35700 35701 |
return SQLITE_DONE;
}
/* Update the page-size to match the value read from the journal.
** Use a testcase() macro to make sure that malloc failure within
** PagerSetPagesize() is tested.
*/
rc = sqlite3PagerSetPagesize(pPager, &iPageSize, -1);
testcase( rc!=SQLITE_OK );
/* Update the assumed sector-size to match the value used by
** the process that created this journal. If this journal was
** created by a process other than this one, then this routine
** is being called from within pager_playback(). The local value
** of Pager.sectorSize is restored at the end of that routine.
*/
|
| ︙ | ︙ | |||
35106 35107 35108 35109 35110 35111 35112 35113 35114 35115 35116 35117 35118 35119 |
int rc; /* Return code */
int nMaster; /* Length of string zMaster */
i64 iHdrOff; /* Offset of header in journal file */
i64 jrnlSize; /* Size of journal file on disk */
u32 cksum = 0; /* Checksum of string zMaster */
assert( pPager->setMaster==0 );
if( !zMaster
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
|| pPager->journalMode==PAGER_JOURNALMODE_OFF
){
return SQLITE_OK;
}
pPager->setMaster = 1;
| > > | 35730 35731 35732 35733 35734 35735 35736 35737 35738 35739 35740 35741 35742 35743 35744 35745 |
int rc; /* Return code */
int nMaster; /* Length of string zMaster */
i64 iHdrOff; /* Offset of header in journal file */
i64 jrnlSize; /* Size of journal file on disk */
u32 cksum = 0; /* Checksum of string zMaster */
assert( pPager->setMaster==0 );
assert( !pagerUseWal(pPager) );
if( !zMaster
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
|| pPager->journalMode==PAGER_JOURNALMODE_OFF
){
return SQLITE_OK;
}
pPager->setMaster = 1;
|
| ︙ | ︙ | |||
35142 35143 35144 35145 35146 35147 35148 |
|| (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster, nMaster)))
|| (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster+4, cksum)))
|| (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, iHdrOff+4+nMaster+8)))
){
return rc;
}
pPager->journalOff += (nMaster+20);
| < | 35768 35769 35770 35771 35772 35773 35774 35775 35776 35777 35778 35779 35780 35781 |
|| (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster, nMaster)))
|| (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster+4, cksum)))
|| (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, iHdrOff+4+nMaster+8)))
){
return rc;
}
pPager->journalOff += (nMaster+20);
/* If the pager is in peristent-journal mode, then the physical
** journal-file may extend past the end of the master-journal name
** and 8 bytes of magic data just written to the file. This is
** dangerous because the code to rollback a hot-journal file
** will not be able to find the master-journal name to determine
** whether or not the journal is hot.
|
| ︙ | ︙ | |||
35178 35179 35180 35181 35182 35183 35184 | ** fail, since no attempt to allocate dynamic memory will be made. */ (void)sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p); return p; } /* | | < < < < | | < < | 35803 35804 35805 35806 35807 35808 35809 35810 35811 35812 35813 35814 35815 35816 35817 35818 35819 35820 35821 |
** fail, since no attempt to allocate dynamic memory will be made.
*/
(void)sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p);
return p;
}
/*
** Discard the entire contents of the in-memory page-cache.
*/
static void pager_reset(Pager *pPager){
sqlite3BackupRestart(pPager->pBackup);
sqlite3PcacheClear(pPager->pPCache);
}
/*
** Free all structures in the Pager.aSavepoint[] array and set both
** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal
** if it is open and the pager is not in exclusive mode.
*/
|
| ︙ | ︙ | |||
35231 35232 35233 35234 35235 35236 35237 |
assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
}
}
return rc;
}
/*
| | | < < < < < < < < < < < < | | | > > | | > | < | | > > > > > > > > > > > > > > > | | | | < | < < < < | < | < < < < < | | < | < > | | < < < < < < | < | > > > > > > > > > > > | > > > > > > | > | | | < | < > > | < | > < < < < < < < < < < < < < < < < < < < < < < < < < < > | | | 35850 35851 35852 35853 35854 35855 35856 35857 35858 35859 35860 35861 35862 35863 35864 35865 35866 35867 35868 35869 35870 35871 35872 35873 35874 35875 35876 35877 35878 35879 35880 35881 35882 35883 35884 35885 35886 35887 35888 35889 35890 35891 35892 35893 35894 35895 35896 35897 35898 35899 35900 35901 35902 35903 35904 35905 35906 35907 35908 35909 35910 35911 35912 35913 35914 35915 35916 35917 35918 35919 35920 35921 35922 35923 35924 35925 35926 35927 35928 35929 35930 35931 35932 35933 35934 35935 35936 35937 35938 35939 35940 35941 35942 35943 35944 35945 35946 35947 35948 35949 35950 35951 35952 35953 35954 35955 35956 35957 35958 35959 35960 35961 35962 35963 35964 35965 35966 35967 35968 35969 35970 35971 35972 35973 35974 35975 35976 35977 35978 35979 35980 35981 35982 35983 35984 35985 35986 35987 35988 35989 35990 35991 35992 35993 35994 35995 35996 |
assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
}
}
return rc;
}
/*
** This function is a no-op if the pager is in exclusive mode and not
** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN
** state.
**
** If the pager is not in exclusive-access mode, the database file is
** completely unlocked. If the file is unlocked and the file-system does
** not exhibit the UNDELETABLE_WHEN_OPEN property, the journal file is
** closed (if it is open).
**
** If the pager is in ERROR state when this function is called, the
** contents of the pager cache are discarded before switching back to
** the OPEN state. Regardless of whether the pager is in exclusive-mode
** or not, any journal file left in the file-system will be treated
** as a hot-journal and rolled back the next time a read-transaction
** is opened (by this or by any other connection).
*/
static void pager_unlock(Pager *pPager){
assert( pPager->eState==PAGER_READER
|| pPager->eState==PAGER_OPEN
|| pPager->eState==PAGER_ERROR
);
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
releaseAllSavepoints(pPager);
if( pagerUseWal(pPager) ){
assert( !isOpen(pPager->jfd) );
sqlite3WalEndReadTransaction(pPager->pWal);
pPager->eState = PAGER_OPEN;
}else if( !pPager->exclusiveMode ){
int rc; /* Error code returned by pagerUnlockDb() */
int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0;
/* If the operating system support deletion of open files, then
** close the journal file when dropping the database lock. Otherwise
** another connection with journal_mode=delete might delete the file
** out from under us.
*/
assert( (PAGER_JOURNALMODE_MEMORY & 5)!=1 );
assert( (PAGER_JOURNALMODE_OFF & 5)!=1 );
assert( (PAGER_JOURNALMODE_WAL & 5)!=1 );
assert( (PAGER_JOURNALMODE_DELETE & 5)!=1 );
assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 );
assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 );
if( 0==(iDc & SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN)
|| 1!=(pPager->journalMode & 5)
){
sqlite3OsClose(pPager->jfd);
}
/* If the pager is in the ERROR state and the call to unlock the database
** file fails, set the current lock to UNKNOWN_LOCK. See the comment
** above the #define for UNKNOWN_LOCK for an explanation of why this
** is necessary.
*/
rc = pagerUnlockDb(pPager, NO_LOCK);
if( rc!=SQLITE_OK && pPager->eState==PAGER_ERROR ){
pPager->eLock = UNKNOWN_LOCK;
}
/* The pager state may be changed from PAGER_ERROR to PAGER_OPEN here
** without clearing the error code. This is intentional - the error
** code is cleared and the cache reset in the block below.
*/
assert( pPager->errCode || pPager->eState!=PAGER_ERROR );
pPager->changeCountDone = 0;
pPager->eState = PAGER_OPEN;
}
/* If Pager.errCode is set, the contents of the pager cache cannot be
** trusted. Now that there are no outstanding references to the pager,
** it can safely move back to PAGER_OPEN state. This happens in both
** normal and exclusive-locking mode.
*/
if( pPager->errCode ){
assert( !MEMDB );
pager_reset(pPager);
pPager->changeCountDone = pPager->tempFile;
pPager->eState = PAGER_OPEN;
pPager->errCode = SQLITE_OK;
}
pPager->journalOff = 0;
pPager->journalHdr = 0;
pPager->setMaster = 0;
}
/*
** This function is called whenever an IOERR or FULL error that requires
** the pager to transition into the ERROR state may ahve occurred.
** The first argument is a pointer to the pager structure, the second
** the error-code about to be returned by a pager API function. The
** value returned is a copy of the second argument to this function.
**
** If the second argument is SQLITE_FULL, SQLITE_IOERR or one of the
** IOERR sub-codes, the pager enters the ERROR state and the error code
** is stored in Pager.errCode. While the pager remains in the ERROR state,
** all major API calls on the Pager will immediately return Pager.errCode.
**
** The ERROR state indicates that the contents of the pager-cache
** cannot be trusted. This state can be cleared by completely discarding
** the contents of the pager-cache. If a transaction was active when
** the persistent error occurred, then the rollback journal may need
** to be replayed to restore the contents of the database file (as if
** it were a hot-journal).
*/
static int pager_error(Pager *pPager, int rc){
int rc2 = rc & 0xff;
assert( rc==SQLITE_OK || !MEMDB );
assert(
pPager->errCode==SQLITE_FULL ||
pPager->errCode==SQLITE_OK ||
(pPager->errCode & 0xff)==SQLITE_IOERR
);
if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){
pPager->errCode = rc;
pPager->eState = PAGER_ERROR;
}
return rc;
}
/*
** This routine ends a transaction. A transaction is usually ended by
** either a COMMIT or a ROLLBACK operation. This routine may be called
** after rollback of a hot-journal, or if an error occurs while opening
** the journal file or writing the very first journal-header of a
** database transaction.
**
** This routine is never called in PAGER_ERROR state. If it is called
** in PAGER_NONE or PAGER_SHARED state and the lock held is less
** exclusive than a RESERVED lock, it is a no-op.
**
** Otherwise, any active savepoints are released.
**
** If the journal file is open, then it is "finalized". Once a journal
** file has been finalized it is not possible to use it to roll back a
** transaction. Nor will it be considered to be a hot-journal by this
** or any other database connection. Exactly how a journal is finalized
|
| ︙ | ︙ | |||
35415 35416 35417 35418 35419 35420 35421 | ** The journal file is closed and deleted using sqlite3OsDelete(). ** ** If the pager is running in exclusive mode, this method of finalizing ** the journal file is never used. Instead, if the journalMode is ** DELETE and the pager is in exclusive mode, the method described under ** journalMode==PERSIST is used instead. ** | | | < < < | < > > > > > > > > > > > > > > | > < > < < < | > | > > > > > > > | > < < < | | | | < > | | > > | < < < > < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 36013 36014 36015 36016 36017 36018 36019 36020 36021 36022 36023 36024 36025 36026 36027 36028 36029 36030 36031 36032 36033 36034 36035 36036 36037 36038 36039 36040 36041 36042 36043 36044 36045 36046 36047 36048 36049 36050 36051 36052 36053 36054 36055 36056 36057 36058 36059 36060 36061 36062 36063 36064 36065 36066 36067 36068 36069 36070 36071 36072 36073 36074 36075 36076 36077 36078 36079 36080 36081 36082 36083 36084 36085 36086 36087 36088 36089 36090 36091 36092 36093 36094 36095 36096 36097 36098 36099 36100 36101 36102 36103 36104 36105 36106 36107 36108 36109 36110 36111 36112 36113 36114 36115 36116 36117 36118 36119 36120 36121 36122 36123 36124 36125 36126 36127 36128 36129 36130 36131 36132 36133 36134 36135 36136 36137 36138 36139 36140 36141 36142 36143 36144 36145 36146 36147 36148 36149 36150 36151 36152 36153 36154 36155 36156 36157 36158 36159 36160 36161 36162 36163 36164 36165 36166 36167 36168 |
** The journal file is closed and deleted using sqlite3OsDelete().
**
** If the pager is running in exclusive mode, this method of finalizing
** the journal file is never used. Instead, if the journalMode is
** DELETE and the pager is in exclusive mode, the method described under
** journalMode==PERSIST is used instead.
**
** After the journal is finalized, the pager moves to PAGER_READER state.
** If running in non-exclusive rollback mode, the lock on the file is
** downgraded to a SHARED_LOCK.
**
** SQLITE_OK is returned if no error occurs. If an error occurs during
** any of the IO operations to finalize the journal file or unlock the
** database then the IO error code is returned to the user. If the
** operation to finalize the journal file fails, then the code still
** tries to unlock the database file if not in exclusive mode. If the
** unlock operation fails as well, then the first error code related
** to the first error encountered (the journal finalization one) is
** returned.
*/
static int pager_end_transaction(Pager *pPager, int hasMaster){
int rc = SQLITE_OK; /* Error code from journal finalization operation */
int rc2 = SQLITE_OK; /* Error code from db file unlock operation */
/* Do nothing if the pager does not have an open write transaction
** or at least a RESERVED lock. This function may be called when there
** is no write-transaction active but a RESERVED or greater lock is
** held under two circumstances:
**
** 1. After a successful hot-journal rollback, it is called with
** eState==PAGER_NONE and eLock==EXCLUSIVE_LOCK.
**
** 2. If a connection with locking_mode=exclusive holding an EXCLUSIVE
** lock switches back to locking_mode=normal and then executes a
** read-transaction, this function is called with eState==PAGER_READER
** and eLock==EXCLUSIVE_LOCK when the read-transaction is closed.
*/
assert( assert_pager_state(pPager) );
assert( pPager->eState!=PAGER_ERROR );
if( pPager->eState<PAGER_WRITER_LOCKED && pPager->eLock<RESERVED_LOCK ){
return SQLITE_OK;
}
releaseAllSavepoints(pPager);
assert( isOpen(pPager->jfd) || pPager->pInJournal==0 );
if( isOpen(pPager->jfd) ){
assert( !pagerUseWal(pPager) );
/* Finalize the journal file. */
if( sqlite3IsMemJournal(pPager->jfd) ){
assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY );
sqlite3OsClose(pPager->jfd);
}else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){
if( pPager->journalOff==0 ){
rc = SQLITE_OK;
}else{
rc = sqlite3OsTruncate(pPager->jfd, 0);
}
pPager->journalOff = 0;
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
|| (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
){
rc = zeroJournalHdr(pPager, hasMaster);
pPager->journalOff = 0;
}else{
/* This branch may be executed with Pager.journalMode==MEMORY if
** a hot-journal was just rolled back. In this case the journal
** file should be closed and deleted. If this connection writes to
** the database file, it will do so using an in-memory journal.
*/
assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
);
sqlite3OsClose(pPager->jfd);
if( !pPager->tempFile ){
rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
}
}
}
#ifdef SQLITE_CHECK_PAGES
sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash);
if( pPager->dbSize==0 && sqlite3PcacheRefCount(pPager->pPCache)>0 ){
PgHdr *p = pager_lookup(pPager, 1);
if( p ){
p->pageHash = 0;
sqlite3PagerUnref(p);
}
}
#endif
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
pPager->nRec = 0;
sqlite3PcacheCleanAll(pPager->pPCache);
sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
if( pagerUseWal(pPager) ){
/* Drop the WAL write-lock, if any. Also, if the connection was in
** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE
** lock held on the database file.
*/
rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
assert( rc2==SQLITE_OK );
}
if( !pPager->exclusiveMode
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
){
rc2 = pagerUnlockDb(pPager, SHARED_LOCK);
pPager->changeCountDone = 0;
}
pPager->eState = PAGER_READER;
pPager->setMaster = 0;
return (rc==SQLITE_OK?rc2:rc);
}
/*
** Execute a rollback if a transaction is active and unlock the
** database file.
**
** If the pager has already entered the ERROR state, do not attempt
** the rollback at this time. Instead, pager_unlock() is called. The
** call to pager_unlock() will discard all in-memory pages, unlock
** the database file and move the pager back to OPEN state. If this
** means that there is a hot-journal left in the file-system, the next
** connection to obtain a shared lock on the pager (which may be this one)
** will roll it back.
**
** If the pager has not already entered the ERROR state, but an IO or
** malloc error occurs during a rollback, then this will itself cause
** the pager to enter the ERROR state. Which will be cleared by the
** call to pager_unlock(), as described above.
*/
static void pagerUnlockAndRollback(Pager *pPager){
if( pPager->eState!=PAGER_ERROR && pPager->eState!=PAGER_OPEN ){
assert( assert_pager_state(pPager) );
if( pPager->eState>=PAGER_WRITER_LOCKED ){
sqlite3BeginBenignMalloc();
sqlite3PagerRollback(pPager);
sqlite3EndBenignMalloc();
}else if( !pPager->exclusiveMode ){
assert( pPager->eState==PAGER_READER );
pager_end_transaction(pPager, 0);
}
}
pager_unlock(pPager);
}
/*
** Parameter aData must point to a buffer of pPager->pageSize bytes
** of data. Compute and return a checksum based ont the contents of the
** page of data and the current value of pPager->cksumInit.
**
** This is not a real checksum. It is really just the sum of the
|
| ︙ | ︙ | |||
35572 35573 35574 35575 35576 35577 35578 | /* ** Read a single page from either the journal file (if isMainJrnl==1) or ** from the sub-journal (if isMainJrnl==0) and playback that page. ** The page begins at offset *pOffset into the file. The *pOffset ** value is increased to the start of the next page in the journal. ** | < | | | 36206 36207 36208 36209 36210 36211 36212 36213 36214 36215 36216 36217 36218 36219 36220 36221 | /* ** Read a single page from either the journal file (if isMainJrnl==1) or ** from the sub-journal (if isMainJrnl==0) and playback that page. ** The page begins at offset *pOffset into the file. The *pOffset ** value is increased to the start of the next page in the journal. ** ** The main rollback journal uses checksums - the statement journal does ** not. ** ** If the page number of the page record read from the (sub-)journal file ** is greater than the current value of Pager.dbSize, then playback is ** skipped and SQLITE_OK is returned. ** ** If pDone is not NULL, then it is a record of pages that have already ** been played back. If the page at *pOffset has already been played back |
| ︙ | ︙ | |||
35627 35628 35629 35630 35631 35632 35633 35634 35635 35636 35637 35638 35639 35640 | assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ assert( isMainJrnl || pDone ); /* pDone always used on sub-journals */ assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */ aData = pPager->pTmpSpace; assert( aData ); /* Temp storage must have already been allocated */ assert( pagerUseWal(pPager)==0 || (!isMainJrnl && isSavepnt) ); /* Read the page number and page data from the journal or sub-journal ** file. Return an error code to the caller if an IO error occurs. */ jfd = isMainJrnl ? pPager->jfd : pPager->sjfd; rc = read32bits(jfd, *pOffset, &pgno); if( rc!=SQLITE_OK ) return rc; | > > > > > > > > > > > | 36260 36261 36262 36263 36264 36265 36266 36267 36268 36269 36270 36271 36272 36273 36274 36275 36276 36277 36278 36279 36280 36281 36282 36283 36284 |
assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */
assert( isMainJrnl || pDone ); /* pDone always used on sub-journals */
assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */
aData = pPager->pTmpSpace;
assert( aData ); /* Temp storage must have already been allocated */
assert( pagerUseWal(pPager)==0 || (!isMainJrnl && isSavepnt) );
/* Either the state is greater than PAGER_WRITER_CACHEMOD (a transaction
** or savepoint rollback done at the request of the caller) or this is
** a hot-journal rollback. If it is a hot-journal rollback, the pager
** is in state OPEN and holds an EXCLUSIVE lock. Hot-journal rollback
** only reads from the main journal, not the sub-journal.
*/
assert( pPager->eState>=PAGER_WRITER_CACHEMOD
|| (pPager->eState==PAGER_OPEN && pPager->eLock==EXCLUSIVE_LOCK)
);
assert( pPager->eState>=PAGER_WRITER_CACHEMOD || isMainJrnl );
/* Read the page number and page data from the journal or sub-journal
** file. Return an error code to the caller if an IO error occurs.
*/
jfd = isMainJrnl ? pPager->jfd : pPager->sjfd;
rc = read32bits(jfd, *pOffset, &pgno);
if( rc!=SQLITE_OK ) return rc;
|
| ︙ | ︙ | |||
35664 35665 35666 35667 35668 35669 35670 |
/* If this page has already been played by before during the current
** rollback, then don't bother to play it back again.
*/
if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){
return rc;
}
| < | | | > > > | 36308 36309 36310 36311 36312 36313 36314 36315 36316 36317 36318 36319 36320 36321 36322 36323 36324 36325 36326 36327 36328 36329 36330 36331 36332 36333 36334 36335 36336 36337 36338 36339 36340 36341 36342 36343 36344 36345 |
/* If this page has already been played by before during the current
** rollback, then don't bother to play it back again.
*/
if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){
return rc;
}
/* When playing back page 1, restore the nReserve setting
*/
if( pgno==1 && pPager->nReserve!=((u8*)aData)[20] ){
pPager->nReserve = ((u8*)aData)[20];
pagerReportSize(pPager);
}
/* If the pager is in CACHEMOD state, then there must be a copy of this
** page in the pager cache. In this case just update the pager cache,
** not the database file. The page is left marked dirty in this case.
**
** An exception to the above rule: If the database is in no-sync mode
** and a page is moved during an incremental vacuum then the page may
** not be in the pager cache. Later: if a malloc() or IO error occurs
** during a Movepage() call, then the page may not be in the cache
** either. So the condition described in the above paragraph is not
** assert()able.
**
** If in WRITER_DBMOD, WRITER_FINISHED or OPEN state, then we update the
** pager cache if it exists and the main file. The page is then marked
** not dirty. Since this code is only executed in PAGER_OPEN state for
** a hot-journal rollback, it is guaranteed that the page-cache is empty
** if the pager is in OPEN state.
**
** Ticket #1171: The statement journal might contain page content that is
** different from the page content at the start of the transaction.
** This occurs when a page is changed prior to the start of a statement
** then changed again within the statement. When rolling back such a
** statement we must not write to the original database unless we know
** for certain that original page contents are synced into the main rollback
|
| ︙ | ︙ | |||
35711 35712 35713 35714 35715 35716 35717 35718 35719 35720 35721 35722 35723 35724 35725 35726 |
*/
if( pagerUseWal(pPager) ){
pPg = 0;
}else{
pPg = pager_lookup(pPager, pgno);
}
assert( pPg || !MEMDB );
PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n",
PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData),
(isMainJrnl?"main-journal":"sub-journal")
));
if( isMainJrnl ){
isSynced = pPager->noSync || (*pOffset <= pPager->journalHdr);
}else{
isSynced = (pPg==0 || 0==(pPg->flags & PGHDR_NEED_SYNC));
}
| > < | > | 36357 36358 36359 36360 36361 36362 36363 36364 36365 36366 36367 36368 36369 36370 36371 36372 36373 36374 36375 36376 36377 36378 36379 36380 36381 36382 |
*/
if( pagerUseWal(pPager) ){
pPg = 0;
}else{
pPg = pager_lookup(pPager, pgno);
}
assert( pPg || !MEMDB );
assert( pPager->eState!=PAGER_OPEN || pPg==0 );
PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n",
PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData),
(isMainJrnl?"main-journal":"sub-journal")
));
if( isMainJrnl ){
isSynced = pPager->noSync || (*pOffset <= pPager->journalHdr);
}else{
isSynced = (pPg==0 || 0==(pPg->flags & PGHDR_NEED_SYNC));
}
if( isOpen(pPager->fd)
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
&& isSynced
){
i64 ofst = (pgno-1)*(i64)pPager->pageSize;
testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 );
assert( !pagerUseWal(pPager) );
rc = sqlite3OsWrite(pPager->fd, (u8*)aData, pPager->pageSize, ofst);
if( pgno>pPager->dbFileSize ){
|
| ︙ | ︙ | |||
35797 35798 35799 35800 35801 35802 35803 |
** be written out into the database file before its journal file
** segment is synced. If a crash occurs during or following this,
** database corruption may ensue.
*/
assert( !pagerUseWal(pPager) );
sqlite3PcacheMakeClean(pPg);
}
| < | | | 36444 36445 36446 36447 36448 36449 36450 36451 36452 36453 36454 36455 36456 36457 36458 36459 |
** be written out into the database file before its journal file
** segment is synced. If a crash occurs during or following this,
** database corruption may ensue.
*/
assert( !pagerUseWal(pPager) );
sqlite3PcacheMakeClean(pPg);
}
pager_set_pagehash(pPg);
/* If this was page 1, then restore the value of Pager.dbFileVers.
** Do this before any decoding. */
if( pgno==1 ){
memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers));
}
/* Decode the page just read from disk */
|
| ︙ | ︙ | |||
35951 35952 35953 35954 35955 35956 35957 | /* ** This function is used to change the actual size of the database ** file in the file-system. This only happens when committing a transaction, ** or rolling back a transaction (including rolling back a hot-journal). ** | | | | | > > > | > > > | 36597 36598 36599 36600 36601 36602 36603 36604 36605 36606 36607 36608 36609 36610 36611 36612 36613 36614 36615 36616 36617 36618 36619 36620 36621 36622 36623 36624 36625 36626 36627 36628 36629 36630 36631 36632 36633 36634 36635 |
/*
** This function is used to change the actual size of the database
** file in the file-system. This only happens when committing a transaction,
** or rolling back a transaction (including rolling back a hot-journal).
**
** If the main database file is not open, or the pager is not in either
** DBMOD or OPEN state, this function is a no-op. Otherwise, the size
** of the file is changed to nPage pages (nPage*pPager->pageSize bytes).
** If the file on disk is currently larger than nPage pages, then use the VFS
** xTruncate() method to truncate it.
**
** Or, it might might be the case that the file on disk is smaller than
** nPage pages. Some operating system implementations can get confused if
** you try to truncate a file to some size that is larger than it
** currently is, so detect this case and write a single zero byte to
** the end of the new file instead.
**
** If successful, return SQLITE_OK. If an IO error occurs while modifying
** the database file, return the error code to the caller.
*/
static int pager_truncate(Pager *pPager, Pgno nPage){
int rc = SQLITE_OK;
assert( pPager->eState!=PAGER_ERROR );
assert( pPager->eState!=PAGER_READER );
if( isOpen(pPager->fd)
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
){
i64 currentSize, newSize;
assert( pPager->eLock==EXCLUSIVE_LOCK );
/* TODO: Is it safe to use Pager.dbFileSize here? */
rc = sqlite3OsFileSize(pPager->fd, ¤tSize);
newSize = pPager->pageSize*(i64)nPage;
if( rc==SQLITE_OK && currentSize!=newSize ){
if( currentSize>newSize ){
rc = sqlite3OsTruncate(pPager->fd, newSize);
}else{
|
| ︙ | ︙ | |||
36093 36094 36095 36096 36097 36098 36099 | int needPagerReset; /* True to reset page prior to first page rollback */ /* Figure out how many records are in the journal. Abort early if ** the journal is empty. */ assert( isOpen(pPager->jfd) ); rc = sqlite3OsFileSize(pPager->jfd, &szJ); | | | 36745 36746 36747 36748 36749 36750 36751 36752 36753 36754 36755 36756 36757 36758 36759 |
int needPagerReset; /* True to reset page prior to first page rollback */
/* Figure out how many records are in the journal. Abort early if
** the journal is empty.
*/
assert( isOpen(pPager->jfd) );
rc = sqlite3OsFileSize(pPager->jfd, &szJ);
if( rc!=SQLITE_OK ){
goto end_playback;
}
/* Read the master journal name from the journal, if it is present.
** If a master journal file name is specified, but the file is not
** present on disk, then the journal is not hot and does not need to be
** played back.
|
| ︙ | ︙ | |||
36127 36128 36129 36130 36131 36132 36133 |
/* This loop terminates either when a readJournalHdr() or
** pager_playback_one_page() call returns SQLITE_DONE or an IO error
** occurs.
*/
while( 1 ){
/* Read the next journal header from the journal file. If there are
** not enough bytes left in the journal file for a complete header, or
| | | 36779 36780 36781 36782 36783 36784 36785 36786 36787 36788 36789 36790 36791 36792 36793 |
/* This loop terminates either when a readJournalHdr() or
** pager_playback_one_page() call returns SQLITE_DONE or an IO error
** occurs.
*/
while( 1 ){
/* Read the next journal header from the journal file. If there are
** not enough bytes left in the journal file for a complete header, or
** it is corrupted, then a process must have failed while writing it.
** This indicates nothing more needs to be rolled back.
*/
rc = readJournalHdr(pPager, isHot, szJ, &nRec, &mxPg);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_DONE ){
rc = SQLITE_OK;
}
|
| ︙ | ︙ | |||
36241 36242 36243 36244 36245 36246 36247 |
pPager->changeCountDone = pPager->tempFile;
if( rc==SQLITE_OK ){
zMaster = pPager->pTmpSpace;
rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
testcase( rc!=SQLITE_OK );
}
| | < < | > | 36893 36894 36895 36896 36897 36898 36899 36900 36901 36902 36903 36904 36905 36906 36907 36908 36909 |
pPager->changeCountDone = pPager->tempFile;
if( rc==SQLITE_OK ){
zMaster = pPager->pTmpSpace;
rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
testcase( rc!=SQLITE_OK );
}
if( rc==SQLITE_OK && !pPager->noSync
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
){
rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
}
if( rc==SQLITE_OK ){
rc = pager_end_transaction(pPager, zMaster[0]!='\0');
testcase( rc!=SQLITE_OK );
}
if( rc==SQLITE_OK && zMaster[0] && res ){
|
| ︙ | ︙ | |||
36286 36287 36288 36289 36290 36291 36292 |
static int readDbPage(PgHdr *pPg){
Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */
Pgno pgno = pPg->pgno; /* Page number to read */
int rc = SQLITE_OK; /* Return code */
int isInWal = 0; /* True if page is in log file */
int pgsz = pPager->pageSize; /* Number of bytes to read */
| | | 36937 36938 36939 36940 36941 36942 36943 36944 36945 36946 36947 36948 36949 36950 36951 |
static int readDbPage(PgHdr *pPg){
Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */
Pgno pgno = pPg->pgno; /* Page number to read */
int rc = SQLITE_OK; /* Return code */
int isInWal = 0; /* True if page is in log file */
int pgsz = pPager->pageSize; /* Number of bytes to read */
assert( pPager->eState>=PAGER_READER && !MEMDB );
assert( isOpen(pPager->fd) );
if( NEVER(!isOpen(pPager->fd)) ){
assert( pPager->tempFile );
memset(pPg->pData, 0, pPager->pageSize);
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
36433 36434 36435 36436 36437 36438 36439 36440 36441 36442 36443 36444 36445 36446 36447 36448 36449 36450 36451 36452 36453 36454 36455 36456 36457 36458 36459 36460 36461 36462 36463 36464 |
);
if( rc==SQLITE_OK && pPager->pBackup ){
PgHdr *p;
for(p=pList; p; p=p->pDirty){
sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData);
}
}
return rc;
}
/*
** Begin a read transaction on the WAL.
**
** This routine used to be called "pagerOpenSnapshot()" because it essentially
** makes a snapshot of the database at the current point in time and preserves
** that snapshot for use by the reader in spite of concurrently changes by
** other writers or checkpointers.
*/
static int pagerBeginReadTransaction(Pager *pPager){
int rc; /* Return code */
int changed = 0; /* True if cache must be reset */
assert( pagerUseWal(pPager) );
/* sqlite3WalEndReadTransaction() was not called for the previous
** transaction in locking_mode=EXCLUSIVE. So call it now. If we
** are in locking_mode=NORMAL and EndRead() was previously called,
** the duplicate call is harmless.
*/
sqlite3WalEndReadTransaction(pPager->pWal);
rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed);
| > > > > > > > > > | < < | > | > | | > > > > > > > > > > > | > > > > > > | > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > < < > > > | | | | < < < | 37084 37085 37086 37087 37088 37089 37090 37091 37092 37093 37094 37095 37096 37097 37098 37099 37100 37101 37102 37103 37104 37105 37106 37107 37108 37109 37110 37111 37112 37113 37114 37115 37116 37117 37118 37119 37120 37121 37122 37123 37124 37125 37126 37127 37128 37129 37130 37131 37132 37133 37134 37135 37136 37137 37138 37139 37140 37141 37142 37143 37144 37145 37146 37147 37148 37149 37150 37151 37152 37153 37154 37155 37156 37157 37158 37159 37160 37161 37162 37163 37164 37165 37166 37167 37168 37169 37170 37171 37172 37173 37174 37175 37176 37177 37178 37179 37180 37181 37182 37183 37184 37185 37186 37187 37188 37189 37190 37191 37192 37193 37194 37195 37196 37197 37198 37199 37200 37201 37202 37203 37204 37205 37206 37207 37208 37209 37210 37211 37212 37213 37214 37215 37216 37217 37218 37219 37220 37221 37222 37223 37224 37225 37226 37227 37228 37229 37230 37231 37232 37233 37234 37235 |
);
if( rc==SQLITE_OK && pPager->pBackup ){
PgHdr *p;
for(p=pList; p; p=p->pDirty){
sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData);
}
}
#ifdef SQLITE_CHECK_PAGES
{
PgHdr *p;
for(p=pList; p; p=p->pDirty) pager_set_pagehash(p);
}
#endif
return rc;
}
/*
** Begin a read transaction on the WAL.
**
** This routine used to be called "pagerOpenSnapshot()" because it essentially
** makes a snapshot of the database at the current point in time and preserves
** that snapshot for use by the reader in spite of concurrently changes by
** other writers or checkpointers.
*/
static int pagerBeginReadTransaction(Pager *pPager){
int rc; /* Return code */
int changed = 0; /* True if cache must be reset */
assert( pagerUseWal(pPager) );
assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
/* sqlite3WalEndReadTransaction() was not called for the previous
** transaction in locking_mode=EXCLUSIVE. So call it now. If we
** are in locking_mode=NORMAL and EndRead() was previously called,
** the duplicate call is harmless.
*/
sqlite3WalEndReadTransaction(pPager->pWal);
rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed);
if( rc==SQLITE_OK && changed ){
pager_reset(pPager);
}
return rc;
}
/*
** This function is called as part of the transition from PAGER_OPEN
** to PAGER_READER state to determine the size of the database file
** in pages (assuming the page size currently stored in Pager.pageSize).
**
** If no error occurs, SQLITE_OK is returned and the size of the database
** in pages is stored in *pnPage. Otherwise, an error code (perhaps
** SQLITE_IOERR_FSTAT) is returned and *pnPage is left unmodified.
*/
static int pagerPagecount(Pager *pPager, Pgno *pnPage){
Pgno nPage; /* Value to return via *pnPage */
/* Query the WAL sub-system for the database size. The WalDbsize()
** function returns zero if the WAL is not open (i.e. Pager.pWal==0), or
** if the database size is not available. The database size is not
** available from the WAL sub-system if the log file is empty or
** contains no valid committed transactions.
*/
assert( pPager->eState==PAGER_OPEN );
assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
nPage = sqlite3WalDbsize(pPager->pWal);
/* If the database size was not available from the WAL sub-system,
** determine it based on the size of the database file. If the size
** of the database file is not an integer multiple of the page-size,
** round down to the nearest page. Except, any file larger than 0
** bytes in size is considered to contain at least one page.
*/
if( nPage==0 ){
i64 n = 0; /* Size of db file in bytes */
assert( isOpen(pPager->fd) || pPager->tempFile );
if( isOpen(pPager->fd) ){
int rc = sqlite3OsFileSize(pPager->fd, &n);
if( rc!=SQLITE_OK ){
return rc;
}
}
nPage = (Pgno)(n / pPager->pageSize);
if( nPage==0 && n>0 ){
nPage = 1;
}
}
/* If the current number of pages in the file is greater than the
** configured maximum pager number, increase the allowed limit so
** that the file can be read.
*/
if( nPage>pPager->mxPgno ){
pPager->mxPgno = (Pgno)nPage;
}
*pnPage = nPage;
return SQLITE_OK;
}
/*
** Check if the *-wal file that corresponds to the database opened by pPager
** exists if the database is not empy, or verify that the *-wal file does
** not exist (by deleting it) if the database file is empty.
**
** If the database is not empty and the *-wal file exists, open the pager
** in WAL mode. If the database is empty or if no *-wal file exists and
** if no error occurs, make sure Pager.journalMode is not set to
** PAGER_JOURNALMODE_WAL.
**
** Return SQLITE_OK or an error code.
**
** The caller must hold a SHARED lock on the database file to call this
** function. Because an EXCLUSIVE lock on the db file is required to delete
** a WAL on a none-empty database, this ensures there is no race condition
** between the xAccess() below and an xDelete() being executed by some
** other connection.
*/
static int pagerOpenWalIfPresent(Pager *pPager){
int rc = SQLITE_OK;
assert( pPager->eState==PAGER_OPEN );
assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
if( !pPager->tempFile ){
int isWal; /* True if WAL file exists */
Pgno nPage; /* Size of the database file */
rc = pagerPagecount(pPager, &nPage);
if( rc ) return rc;
if( nPage==0 ){
rc = sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0);
isWal = 0;
}else{
rc = sqlite3OsAccess(
pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &isWal
);
}
if( rc==SQLITE_OK ){
if( isWal ){
testcase( sqlite3PcachePagecount(pPager->pPCache)==0 );
rc = sqlite3PagerOpenWal(pPager, 0);
}else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){
pPager->journalMode = PAGER_JOURNALMODE_DELETE;
}
}
}
return rc;
}
|
| ︙ | ︙ | |||
36565 36566 36567 36568 36569 36570 36571 |
*/
static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
i64 szJ; /* Effective size of the main journal */
i64 iHdrOff; /* End of first segment of main-journal records */
int rc = SQLITE_OK; /* Return code */
Bitvec *pDone = 0; /* Bitvec to ensure pages played back only once */
| | > | 37273 37274 37275 37276 37277 37278 37279 37280 37281 37282 37283 37284 37285 37286 37287 37288 |
*/
static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
i64 szJ; /* Effective size of the main journal */
i64 iHdrOff; /* End of first segment of main-journal records */
int rc = SQLITE_OK; /* Return code */
Bitvec *pDone = 0; /* Bitvec to ensure pages played back only once */
assert( pPager->eState!=PAGER_ERROR );
assert( pPager->eState>=PAGER_WRITER_LOCKED );
/* Allocate a bitvec to use to store the set of pages rolled back */
if( pSavepoint ){
pDone = sqlite3BitvecCreate(pSavepoint->nOrig);
if( !pDone ){
return SQLITE_NOMEM;
}
|
| ︙ | ︙ | |||
36704 36705 36706 36707 36708 36709 36710 |
** and FULL=3.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int bFullFsync){
pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
pPager->sync_flags = (bFullFsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL);
| < | 37413 37414 37415 37416 37417 37418 37419 37420 37421 37422 37423 37424 37425 37426 |
** and FULL=3.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int bFullFsync){
pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
pPager->sync_flags = (bFullFsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL);
}
#endif
/*
** The following global variable is incremented whenever the library
** attempts to open a temporary file. This information is used for
** testing and analysis only.
|
| ︙ | ︙ | |||
36786 36787 36788 36789 36790 36791 36792 | /* ** Change the page size used by the Pager object. The new page size ** is passed in *pPageSize. ** ** If the pager is in the error state when this function is called, it ** is a no-op. The value returned is the error state error code (i.e. | | | 37494 37495 37496 37497 37498 37499 37500 37501 37502 37503 37504 37505 37506 37507 37508 | /* ** Change the page size used by the Pager object. The new page size ** is passed in *pPageSize. ** ** If the pager is in the error state when this function is called, it ** is a no-op. The value returned is the error state error code (i.e. ** one of SQLITE_IOERR, an SQLITE_IOERR_xxx sub-code or SQLITE_FULL). ** ** Otherwise, if all of the following are true: ** ** * the new page size (value of *pPageSize) is valid (a power ** of two between 512 and SQLITE_MAX_PAGE_SIZE, inclusive), and ** ** * there are no outstanding page references, and |
| ︙ | ︙ | |||
36810 36811 36812 36813 36814 36815 36816 | ** In all other cases, SQLITE_OK is returned. ** ** If the page size is not changed, either because one of the enumerated ** conditions above is not true, the pager was in error state when this ** function was called, or because the memory allocation attempt failed, ** then *pPageSize is set to the old, retained page size before returning. */ | | | > > > > > > > > > | | | | | | | > > > > > > > | | > > | < | > | | | | | | > | > | 37518 37519 37520 37521 37522 37523 37524 37525 37526 37527 37528 37529 37530 37531 37532 37533 37534 37535 37536 37537 37538 37539 37540 37541 37542 37543 37544 37545 37546 37547 37548 37549 37550 37551 37552 37553 37554 37555 37556 37557 37558 37559 37560 37561 37562 37563 37564 37565 37566 37567 37568 37569 37570 37571 37572 37573 |
** In all other cases, SQLITE_OK is returned.
**
** If the page size is not changed, either because one of the enumerated
** conditions above is not true, the pager was in error state when this
** function was called, or because the memory allocation attempt failed,
** then *pPageSize is set to the old, retained page size before returning.
*/
SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){
int rc = SQLITE_OK;
/* It is not possible to do a full assert_pager_state() here, as this
** function may be called from within PagerOpen(), before the state
** of the Pager object is internally consistent.
**
** At one point this function returned an error if the pager was in
** PAGER_ERROR state. But since PAGER_ERROR state guarantees that
** there is at least one outstanding page reference, this function
** is a no-op for that case anyhow.
*/
u32 pageSize = *pPageSize;
assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
if( (pPager->memDb==0 || pPager->dbSize==0)
&& sqlite3PcacheRefCount(pPager->pPCache)==0
&& pageSize && pageSize!=(u32)pPager->pageSize
){
char *pNew = NULL; /* New temp space */
i64 nByte = 0;
if( pPager->eState>PAGER_OPEN && isOpen(pPager->fd) ){
rc = sqlite3OsFileSize(pPager->fd, &nByte);
}
if( rc==SQLITE_OK ){
pNew = (char *)sqlite3PageMalloc(pageSize);
if( !pNew ) rc = SQLITE_NOMEM;
}
if( rc==SQLITE_OK ){
pager_reset(pPager);
pPager->dbSize = (Pgno)(nByte/pageSize);
pPager->pageSize = pageSize;
sqlite3PageFree(pPager->pTmpSpace);
pPager->pTmpSpace = pNew;
sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
}
}
*pPageSize = pPager->pageSize;
if( rc==SQLITE_OK ){
if( nReserve<0 ) nReserve = pPager->nReserve;
assert( nReserve>=0 && nReserve<1000 );
pPager->nReserve = (i16)nReserve;
pagerReportSize(pPager);
}
return rc;
}
|
| ︙ | ︙ | |||
36860 36861 36862 36863 36864 36865 36866 |
** 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 int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){
| < | < | | 37588 37589 37590 37591 37592 37593 37594 37595 37596 37597 37598 37599 37600 37601 37602 37603 37604 37605 37606 |
** 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 int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){
if( mxPage>0 ){
pPager->mxPgno = mxPage;
}
if( pPager->eState!=PAGER_OPEN && pPager->mxPgno<pPager->dbSize ){
pPager->mxPgno = pPager->dbSize;
}
return pPager->mxPgno;
}
/*
** The following set of routines are used to disable the simulated
** I/O error mechanism. These routines are used to avoid simulated
|
| ︙ | ︙ | |||
36931 36932 36933 36934 36935 36936 36937 |
rc = SQLITE_OK;
}
}
return rc;
}
/*
| > | < > < < < < < < < < < | < | < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < | | | < < < | | | < < < < < | 37657 37658 37659 37660 37661 37662 37663 37664 37665 37666 37667 37668 37669 37670 37671 37672 37673 37674 37675 37676 37677 37678 37679 37680 37681 37682 37683 37684 37685 37686 37687 37688 37689 37690 37691 37692 37693 37694 37695 37696 37697 37698 37699 37700 37701 37702 37703 37704 37705 37706 37707 37708 37709 37710 37711 37712 37713 |
rc = SQLITE_OK;
}
}
return rc;
}
/*
** This function may only be called when a read-transaction is open on
** the pager. It returns the total number of pages in the database.
**
** However, if the file is between 1 and <page-size> bytes in size, then
** this is considered a 1 page file.
*/
SQLITE_PRIVATE void sqlite3PagerPagecount(Pager *pPager, int *pnPage){
assert( pPager->eState>=PAGER_READER );
assert( pPager->eState!=PAGER_WRITER_FINISHED );
*pnPage = (int)pPager->dbSize;
}
/*
** Try to obtain a lock of type locktype on the database file. If
** a similar or greater lock is already held, this function is a no-op
** (returning SQLITE_OK immediately).
**
** Otherwise, attempt to obtain the lock using sqlite3OsLock(). Invoke
** the busy callback if the lock is currently not available. Repeat
** until the busy callback returns false or until the attempt to
** obtain the lock succeeds.
**
** Return SQLITE_OK on success and an error code if we cannot obtain
** the lock. If the lock is obtained successfully, set the Pager.state
** variable to locktype before returning.
*/
static int pager_wait_on_lock(Pager *pPager, int locktype){
int rc; /* Return code */
/* Check that this is either a no-op (because the requested lock is
** already held, or one of the transistions that the busy-handler
** may be invoked during, according to the comment above
** sqlite3PagerSetBusyhandler().
*/
assert( (pPager->eLock>=locktype)
|| (pPager->eLock==NO_LOCK && locktype==SHARED_LOCK)
|| (pPager->eLock==RESERVED_LOCK && locktype==EXCLUSIVE_LOCK)
);
do {
rc = pagerLockDb(pPager, locktype);
}while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) );
return rc;
}
/*
** Function assertTruncateConstraint(pPager) checks that one of the
** following is true for all dirty pages currently in the page-cache:
**
|
| ︙ | ︙ | |||
37087 37088 37089 37090 37091 37092 37093 |
/*
** Truncate the in-memory database file image to nPage pages. This
** function does not actually modify the database file on disk. It
** just sets the internal state of the pager object so that the
** truncation will be done when the current transaction is committed.
*/
SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){
| < | | 37744 37745 37746 37747 37748 37749 37750 37751 37752 37753 37754 37755 37756 37757 37758 37759 |
/*
** Truncate the in-memory database file image to nPage pages. This
** function does not actually modify the database file on disk. It
** just sets the internal state of the pager object so that the
** truncation will be done when the current transaction is committed.
*/
SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){
assert( pPager->dbSize>=nPage );
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
pPager->dbSize = nPage;
assertTruncateConstraint(pPager);
}
/*
** This function is called before attempting a hot-journal rollback. It
|
| ︙ | ︙ | |||
37139 37140 37141 37142 37143 37144 37145 |
** to the caller.
*/
SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager){
u8 *pTmp = (u8 *)pPager->pTmpSpace;
disable_simulated_io_errors();
sqlite3BeginBenignMalloc();
| | < | | | | > > > > > > | | 37795 37796 37797 37798 37799 37800 37801 37802 37803 37804 37805 37806 37807 37808 37809 37810 37811 37812 37813 37814 37815 37816 37817 37818 37819 37820 37821 37822 37823 37824 37825 37826 37827 37828 37829 37830 37831 37832 37833 37834 |
** to the caller.
*/
SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager){
u8 *pTmp = (u8 *)pPager->pTmpSpace;
disable_simulated_io_errors();
sqlite3BeginBenignMalloc();
/* pPager->errCode = 0; */
pPager->exclusiveMode = 0;
#ifndef SQLITE_OMIT_WAL
sqlite3WalClose(pPager->pWal,
(pPager->noSync ? 0 : pPager->sync_flags),
pPager->pageSize, pTmp
);
pPager->pWal = 0;
#endif
pager_reset(pPager);
if( MEMDB ){
pager_unlock(pPager);
}else{
/* If it is open, sync the journal file before calling UnlockAndRollback.
** If this is not done, then an unsynced portion of the open journal
** file may be played back into the database. If a power failure occurs
** while this is happening, the database could become corrupt.
**
** If an error occurs while trying to sync the journal, shift the pager
** into the ERROR state. This causes UnlockAndRollback to unlock the
** database and close the journal file without attempting to roll it
** back or finalize it. The next database user will have to do hot-journal
** rollback before accessing the database file.
*/
if( isOpen(pPager->jfd) ){
pager_error(pPager, pagerSyncHotJournal(pPager));
}
pagerUnlockAndRollback(pPager);
}
sqlite3EndBenignMalloc();
enable_simulated_io_errors();
PAGERTRACE(("CLOSE %d\n", PAGERID(pPager)));
IOTRACE(("CLOSE %p\n", pPager))
|
| ︙ | ︙ | |||
37204 37205 37206 37207 37208 37209 37210 | } /* ** Sync the journal. In other words, make sure all the pages that have ** been written to the journal have actually reached the surface of the ** disk and can be restored in the event of a hot-journal rollback. ** | | | | | 37865 37866 37867 37868 37869 37870 37871 37872 37873 37874 37875 37876 37877 37878 37879 37880 37881 | } /* ** Sync the journal. In other words, make sure all the pages that have ** been written to the journal have actually reached the surface of the ** disk and can be restored in the event of a hot-journal rollback. ** ** If the Pager.noSync flag is set, then this function is a no-op. ** Otherwise, the actions required depend on the journal-mode and the ** device characteristics of the the file-system, as follows: ** ** * If the journal file is an in-memory journal file, no action need ** be taken. ** ** * Otherwise, if the device does not support the SAFE_APPEND property, ** then the nRec field of the most recently written journal header ** is updated to contain the number of journal records that have |
| ︙ | ︙ | |||
37230 37231 37232 37233 37234 37235 37236 |
** if( NOT SAFE_APPEND ){
** if( <full-sync mode> ) xSync(<journal file>);
** <update nRec field>
** }
** if( NOT SEQUENTIAL ) xSync(<journal file>);
** }
**
| < < < | > > > > > > > > > > > | | < | 37891 37892 37893 37894 37895 37896 37897 37898 37899 37900 37901 37902 37903 37904 37905 37906 37907 37908 37909 37910 37911 37912 37913 37914 37915 37916 37917 37918 37919 37920 37921 37922 37923 |
** if( NOT SAFE_APPEND ){
** if( <full-sync mode> ) xSync(<journal file>);
** <update nRec field>
** }
** if( NOT SEQUENTIAL ) xSync(<journal file>);
** }
**
** If successful, this routine clears the PGHDR_NEED_SYNC flag of every
** page currently held in memory before returning SQLITE_OK. If an IO
** error is encountered, then the IO error code is returned to the caller.
*/
static int syncJournal(Pager *pPager, int newHdr){
int rc; /* Return code */
assert( pPager->eState==PAGER_WRITER_CACHEMOD
|| pPager->eState==PAGER_WRITER_DBMOD
);
assert( assert_pager_state(pPager) );
assert( !pagerUseWal(pPager) );
rc = sqlite3PagerExclusiveLock(pPager);
if( rc!=SQLITE_OK ) return rc;
if( !pPager->noSync ){
assert( !pPager->tempFile );
if( isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){
const int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
assert( isOpen(pPager->jfd) );
if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
/* This block deals with an obscure problem. If the last connection
** that wrote to this database was operating in persistent-journal
** mode, then the journal file may at this point actually be larger
|
| ︙ | ︙ | |||
37316 37317 37318 37319 37320 37321 37322 |
PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
IOTRACE(("JSYNC %p\n", pPager))
rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags|
(pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
);
if( rc!=SQLITE_OK ) return rc;
}
| | | | < < | | > > > | < | | > > > > > > > > | 37984 37985 37986 37987 37988 37989 37990 37991 37992 37993 37994 37995 37996 37997 37998 37999 38000 38001 38002 38003 38004 38005 38006 38007 38008 38009 38010 38011 38012 38013 38014 38015 38016 |
PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
IOTRACE(("JSYNC %p\n", pPager))
rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags|
(pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
);
if( rc!=SQLITE_OK ) return rc;
}
pPager->journalHdr = pPager->journalOff;
if( newHdr && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
pPager->nRec = 0;
rc = writeJournalHdr(pPager);
if( rc!=SQLITE_OK ) return rc;
}
}else{
pPager->journalHdr = pPager->journalOff;
}
}
/* Unless the pager is in noSync mode, the journal file was just
** successfully synced. Either way, clear the PGHDR_NEED_SYNC flag on
** all pages.
*/
sqlite3PcacheClearSyncFlags(pPager->pPCache);
pPager->eState = PAGER_WRITER_DBMOD;
assert( assert_pager_state(pPager) );
return SQLITE_OK;
}
/*
** The argument is the first in a linked list of dirty pages connected
** by the PgHdr.pDirty pointer. This function writes each one of the
** in-memory pages in the list to the database file. The argument may
|
| ︙ | ︙ | |||
37363 37364 37365 37366 37367 37368 37369 |
** the database file.
**
** If everything is successful, SQLITE_OK is returned. If an IO error
** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot
** be obtained, SQLITE_BUSY is returned.
*/
static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
| | | < < < < < < < < < < < < < < < | | | > > > | 38039 38040 38041 38042 38043 38044 38045 38046 38047 38048 38049 38050 38051 38052 38053 38054 38055 38056 38057 38058 38059 38060 38061 38062 38063 38064 38065 38066 38067 38068 38069 38070 38071 38072 38073 38074 38075 38076 38077 38078 38079 38080 38081 38082 38083 38084 38085 38086 38087 38088 38089 38090 38091 38092 38093 38094 |
** the database file.
**
** If everything is successful, SQLITE_OK is returned. If an IO error
** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot
** be obtained, SQLITE_BUSY is returned.
*/
static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
int rc = SQLITE_OK; /* Return code */
/* This function is only called for rollback pagers in WRITER_DBMOD state. */
assert( !pagerUseWal(pPager) );
assert( pPager->eState==PAGER_WRITER_DBMOD );
assert( pPager->eLock==EXCLUSIVE_LOCK );
/* If the file is a temp-file has not yet been opened, open it now. It
** is not possible for rc to be other than SQLITE_OK if this branch
** is taken, as pager_wait_on_lock() is a no-op for temp-files.
*/
if( !isOpen(pPager->fd) ){
assert( pPager->tempFile && rc==SQLITE_OK );
rc = pagerOpentemp(pPager, pPager->fd, pPager->vfsFlags);
}
/* Before the first write, give the VFS a hint of what the final
** file size will be.
*/
assert( rc!=SQLITE_OK || isOpen(pPager->fd) );
if( rc==SQLITE_OK && pPager->dbSize>pPager->dbHintSize ){
sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize;
sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile);
pPager->dbHintSize = pPager->dbSize;
}
while( rc==SQLITE_OK && pList ){
Pgno pgno = pList->pgno;
/* If there are dirty pages in the page cache with page numbers greater
** than Pager.dbSize, this means sqlite3PagerTruncateImage() was called to
** make the file smaller (presumably by auto-vacuum code). Do not write
** any such pages to the file.
**
** Also, do not write out any page that has the PGHDR_DONT_WRITE flag
** set (set by sqlite3PagerDontWrite()).
*/
if( pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){
i64 offset = (pgno-1)*(i64)pPager->pageSize; /* Offset to write */
char *pData; /* Data to write */
assert( (pList->flags&PGHDR_NEED_SYNC)==0 );
/* Encode the database */
CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData);
/* Write out the page data. */
rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset);
|
| ︙ | ︙ | |||
37446 37447 37448 37449 37450 37451 37452 |
PAGERID(pPager), pgno, pager_pagehash(pList)));
IOTRACE(("PGOUT %p %d\n", pPager, pgno));
PAGER_INCR(sqlite3_pager_writedb_count);
PAGER_INCR(pPager->nWrite);
}else{
PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno));
}
| < | < | 38110 38111 38112 38113 38114 38115 38116 38117 38118 38119 38120 38121 38122 38123 38124 |
PAGERID(pPager), pgno, pager_pagehash(pList)));
IOTRACE(("PGOUT %p %d\n", pPager, pgno));
PAGER_INCR(sqlite3_pager_writedb_count);
PAGER_INCR(pPager->nWrite);
}else{
PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno));
}
pager_set_pagehash(pList);
pList = pList->pDirty;
}
return rc;
}
/*
|
| ︙ | ︙ | |||
37560 37561 37562 37563 37564 37565 37566 | ** journal (and adding a new header) is not allowed. This occurs ** during calls to sqlite3PagerWrite() while trying to journal multiple ** pages belonging to the same sector. ** ** The doNotSpill flag inhibits all cache spilling regardless of whether ** or not a sync is required. This is set during a rollback. ** | | > > > > > | | | < < < < | | < < | 38222 38223 38224 38225 38226 38227 38228 38229 38230 38231 38232 38233 38234 38235 38236 38237 38238 38239 38240 38241 38242 38243 38244 38245 38246 38247 38248 38249 38250 38251 38252 38253 38254 38255 38256 38257 38258 38259 38260 38261 38262 38263 38264 |
** journal (and adding a new header) is not allowed. This occurs
** during calls to sqlite3PagerWrite() while trying to journal multiple
** pages belonging to the same sector.
**
** The doNotSpill flag inhibits all cache spilling regardless of whether
** or not a sync is required. This is set during a rollback.
**
** Spilling is also prohibited when in an error state since that could
** lead to database corruption. In the current implementaton it
** is impossible for sqlite3PCacheFetch() to be called with createFlag==1
** while in the error state, hence it is impossible for this routine to
** be called in the error state. Nevertheless, we include a NEVER()
** test for the error state as a safeguard against future changes.
*/
if( NEVER(pPager->errCode) ) return SQLITE_OK;
if( pPager->doNotSpill ) return SQLITE_OK;
if( pPager->doNotSyncSpill && (pPg->flags & PGHDR_NEED_SYNC)!=0 ){
return SQLITE_OK;
}
pPg->pDirty = 0;
if( pagerUseWal(pPager) ){
/* Write a single frame for this page to the log. */
if( subjRequiresPage(pPg) ){
rc = subjournalPage(pPg);
}
if( rc==SQLITE_OK ){
rc = pagerWalFrames(pPager, pPg, 0, 0, 0);
}
}else{
/* Sync the journal file if required. */
if( pPg->flags&PGHDR_NEED_SYNC
|| pPager->eState==PAGER_WRITER_CACHEMOD
){
rc = syncJournal(pPager, 1);
}
/* If the page number of this page is larger than the current size of
** the database image, it may need to be written to the sub-journal.
** This is because the call to pager_write_pagelist() below will not
** actually write data to the file in this case.
**
|
| ︙ | ︙ | |||
37627 37628 37629 37630 37631 37632 37633 37634 37635 37636 37637 37638 37639 37640 37641 37642 37643 |
rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg)
) ){
rc = subjournalPage(pPg);
}
/* Write the contents of the page out to the database file. */
if( rc==SQLITE_OK ){
rc = pager_write_pagelist(pPager, pPg);
}
}
/* Mark the page as clean. */
if( rc==SQLITE_OK ){
PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno));
sqlite3PcacheMakeClean(pPg);
}
| > | | 38288 38289 38290 38291 38292 38293 38294 38295 38296 38297 38298 38299 38300 38301 38302 38303 38304 38305 38306 38307 38308 38309 38310 38311 38312 38313 |
rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg)
) ){
rc = subjournalPage(pPg);
}
/* Write the contents of the page out to the database file. */
if( rc==SQLITE_OK ){
assert( (pPg->flags&PGHDR_NEED_SYNC)==0 );
rc = pager_write_pagelist(pPager, pPg);
}
}
/* Mark the page as clean. */
if( rc==SQLITE_OK ){
PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno));
sqlite3PcacheMakeClean(pPg);
}
return pager_error(pPager, rc);
}
/*
** Allocate and initialize a new Pager object and put a pointer to it
** in *ppPager. The pager should eventually be freed by passing it
** to sqlite3PagerClose().
|
| ︙ | ︙ | |||
37692 37693 37694 37695 37696 37697 37698 | int readOnly = 0; /* True if this is a read-only file */ int journalFileSize; /* Bytes to allocate for each journal fd */ char *zPathname = 0; /* Full path to database file */ int nPathname = 0; /* Number of bytes in zPathname */ int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */ int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */ int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */ | | | 38354 38355 38356 38357 38358 38359 38360 38361 38362 38363 38364 38365 38366 38367 38368 | int readOnly = 0; /* True if this is a read-only file */ int journalFileSize; /* Bytes to allocate for each journal fd */ char *zPathname = 0; /* Full path to database file */ int nPathname = 0; /* Number of bytes in zPathname */ int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */ int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */ int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */ u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ /* Figure out how much space is required for each journal file-handle ** (there are two of them, the main journal and the sub-journal). This ** is the maximum space required for an in-memory journal file handle ** and a regular journal file-handle. Note that a "regular journal-handle" ** may be a wrapper capable of caching the first portion of the journal ** file in memory to implement the atomic-write optimization (see |
| ︙ | ︙ | |||
37827 37828 37829 37830 37831 37832 37833 |
if( rc==SQLITE_OK && !readOnly ){
setSectorSize(pPager);
assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
if( szPageDflt<pPager->sectorSize ){
if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
}else{
| | | 38489 38490 38491 38492 38493 38494 38495 38496 38497 38498 38499 38500 38501 38502 38503 |
if( rc==SQLITE_OK && !readOnly ){
setSectorSize(pPager);
assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
if( szPageDflt<pPager->sectorSize ){
if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
}else{
szPageDflt = (u32)pPager->sectorSize;
}
}
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
{
int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
int ii;
assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
|
| ︙ | ︙ | |||
37855 37856 37857 37858 37859 37860 37861 |
** opening the file until the first call to OsWrite().
**
** This branch is also run for an in-memory database. An in-memory
** database is the same as a temp-file that is never written out to
** disk and uses an in-memory rollback journal.
*/
tempFile = 1;
| | > | 38517 38518 38519 38520 38521 38522 38523 38524 38525 38526 38527 38528 38529 38530 38531 38532 |
** opening the file until the first call to OsWrite().
**
** This branch is also run for an in-memory database. An in-memory
** database is the same as a temp-file that is never written out to
** disk and uses an in-memory rollback journal.
*/
tempFile = 1;
pPager->eState = PAGER_READER;
pPager->eLock = EXCLUSIVE_LOCK;
readOnly = (vfsFlags&SQLITE_OPEN_READONLY);
}
/* The following call to PagerSetPagesize() serves to set the value of
** Pager.pageSize and to allocate the Pager.pTmpSpace buffer.
*/
if( rc==SQLITE_OK ){
|
| ︙ | ︙ | |||
37892 37893 37894 37895 37896 37897 37898 |
IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename))
pPager->useJournal = (u8)useJournal;
pPager->noReadlock = (noReadlock && readOnly) ?1:0;
/* pPager->stmtOpen = 0; */
/* pPager->stmtInUse = 0; */
/* pPager->nRef = 0; */
| < > > < | 38555 38556 38557 38558 38559 38560 38561 38562 38563 38564 38565 38566 38567 38568 38569 38570 38571 38572 38573 38574 38575 38576 38577 38578 38579 38580 38581 38582 38583 38584 38585 |
IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename))
pPager->useJournal = (u8)useJournal;
pPager->noReadlock = (noReadlock && readOnly) ?1:0;
/* pPager->stmtOpen = 0; */
/* pPager->stmtInUse = 0; */
/* pPager->nRef = 0; */
/* pPager->stmtSize = 0; */
/* pPager->stmtJSize = 0; */
/* pPager->nPage = 0; */
pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
/* pPager->state = PAGER_UNLOCK; */
#if 0
assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
#endif
/* pPager->errMask = 0; */
pPager->tempFile = (u8)tempFile;
assert( tempFile==PAGER_LOCKINGMODE_NORMAL
|| tempFile==PAGER_LOCKINGMODE_EXCLUSIVE );
assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 );
pPager->exclusiveMode = (u8)tempFile;
pPager->changeCountDone = pPager->tempFile;
pPager->memDb = (u8)memDb;
pPager->readOnly = (u8)readOnly;
assert( useJournal || pPager->tempFile );
pPager->noSync = pPager->tempFile;
pPager->fullSync = pPager->noSync ?0:1;
pPager->sync_flags = SQLITE_SYNC_NORMAL;
/* pPager->pFirst = 0; */
/* pPager->pFirstSynced = 0; */
/* pPager->pLast = 0; */
|
| ︙ | ︙ | |||
37973 37974 37975 37976 37977 37978 37979 |
*/
static int hasHotJournal(Pager *pPager, int *pExists){
sqlite3_vfs * const pVfs = pPager->pVfs;
int rc = SQLITE_OK; /* Return code */
int exists = 1; /* True if a journal file is present */
int jrnlOpen = !!isOpen(pPager->jfd);
| < | > | | | | | | 38636 38637 38638 38639 38640 38641 38642 38643 38644 38645 38646 38647 38648 38649 38650 38651 38652 38653 38654 38655 38656 38657 38658 38659 38660 38661 38662 38663 38664 38665 38666 38667 38668 38669 38670 38671 38672 38673 38674 38675 38676 38677 38678 38679 38680 38681 38682 38683 38684 38685 38686 38687 38688 38689 |
*/
static int hasHotJournal(Pager *pPager, int *pExists){
sqlite3_vfs * const pVfs = pPager->pVfs;
int rc = SQLITE_OK; /* Return code */
int exists = 1; /* True if a journal file is present */
int jrnlOpen = !!isOpen(pPager->jfd);
assert( pPager->useJournal );
assert( isOpen(pPager->fd) );
assert( pPager->eState==PAGER_OPEN );
assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) &
SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
));
*pExists = 0;
if( !jrnlOpen ){
rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
}
if( rc==SQLITE_OK && exists ){
int locked = 0; /* True if some process holds a RESERVED lock */
/* Race condition here: Another process might have been holding the
** the RESERVED lock and have a journal open at the sqlite3OsAccess()
** call above, but then delete the journal and drop the lock before
** we get to the following sqlite3OsCheckReservedLock() call. If that
** is the case, this routine might think there is a hot journal when
** in fact there is none. This results in a false-positive which will
** be dealt with by the playback routine. Ticket #3883.
*/
rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
if( rc==SQLITE_OK && !locked ){
Pgno nPage; /* Number of pages in database file */
/* Check the size of the database file. If it consists of 0 pages,
** then delete the journal file. See the header comment above for
** the reasoning here. Delete the obsolete journal file under
** a RESERVED lock to avoid race conditions and to avoid violating
** [H33020].
*/
rc = pagerPagecount(pPager, &nPage);
if( rc==SQLITE_OK ){
if( nPage==0 ){
sqlite3BeginBenignMalloc();
if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){
sqlite3OsDelete(pVfs, pPager->zJournal, 0);
pagerUnlockDb(pPager, SHARED_LOCK);
}
sqlite3EndBenignMalloc();
}else{
/* The journal file exists and no other connection has a reserved
** or greater lock on the database file. Now check that there is
** at least one non-zero bytes at the start of the journal file.
** If there is, then we consider this journal to be hot. If not,
|
| ︙ | ︙ | |||
38065 38066 38067 38068 38069 38070 38071 | ** This function is called to obtain a shared lock on the database file. ** It is illegal to call sqlite3PagerAcquire() until after this function ** has been successfully called. If a shared-lock is already held when ** this function is called, it is a no-op. ** ** The following operations are also performed by this function. ** | | < < < < < | | | < | > > > > > < < < < | | < | < < < < < < < < < > | | < < < | < > < < | | > | | | < | > > > < | | < | | < | | | | | | > | > > > > > > | | > | < | | | | | | | | | < < < < < < < | | < < < < < < < < < < < < < > > > > > > | > > > > > > > > > > > > > > > > | | | | > | | > | > | < < | | < < > > > > > > > > > > | > > > | < < | 38728 38729 38730 38731 38732 38733 38734 38735 38736 38737 38738 38739 38740 38741 38742 38743 38744 38745 38746 38747 38748 38749 38750 38751 38752 38753 38754 38755 38756 38757 38758 38759 38760 38761 38762 38763 38764 38765 38766 38767 38768 38769 38770 38771 38772 38773 38774 38775 38776 38777 38778 38779 38780 38781 38782 38783 38784 38785 38786 38787 38788 38789 38790 38791 38792 38793 38794 38795 38796 38797 38798 38799 38800 38801 38802 38803 38804 38805 38806 38807 38808 38809 38810 38811 38812 38813 38814 38815 38816 38817 38818 38819 38820 38821 38822 38823 38824 38825 38826 38827 38828 38829 38830 38831 38832 38833 38834 38835 38836 38837 38838 38839 38840 38841 38842 38843 38844 38845 38846 38847 38848 38849 38850 38851 38852 38853 38854 38855 38856 38857 38858 38859 38860 38861 38862 38863 38864 38865 38866 38867 38868 38869 38870 38871 38872 38873 38874 38875 38876 38877 38878 38879 38880 38881 38882 38883 38884 38885 38886 38887 38888 38889 38890 38891 38892 38893 38894 38895 38896 38897 38898 38899 38900 38901 38902 38903 38904 38905 38906 38907 38908 38909 38910 38911 38912 38913 38914 38915 38916 38917 38918 38919 38920 38921 38922 38923 38924 38925 38926 38927 38928 38929 38930 38931 38932 38933 38934 38935 38936 38937 38938 38939 38940 38941 38942 38943 38944 38945 38946 38947 38948 38949 38950 38951 38952 38953 38954 38955 38956 38957 38958 38959 38960 38961 38962 38963 38964 38965 38966 38967 38968 38969 38970 38971 |
** This function is called to obtain a shared lock on the database file.
** It is illegal to call sqlite3PagerAcquire() until after this function
** has been successfully called. If a shared-lock is already held when
** this function is called, it is a no-op.
**
** The following operations are also performed by this function.
**
** 1) If the pager is currently in PAGER_OPEN state (no lock held
** on the database file), then an attempt is made to obtain a
** SHARED lock on the database file. Immediately after obtaining
** the SHARED lock, the file-system is checked for a hot-journal,
** which is played back if present. Following any hot-journal
** rollback, the contents of the cache are validated by checking
** the 'change-counter' field of the database file header and
** discarded if they are found to be invalid.
**
** 2) If the pager is running in exclusive-mode, and there are currently
** no outstanding references to any pages, and is in the error state,
** then an attempt is made to clear the error state by discarding
** the contents of the page cache and rolling back any open journal
** file.
**
** If everything is successful, SQLITE_OK is returned. If an IO error
** occurs while locking the database, checking for a hot-journal file or
** rolling back a journal file, the IO error code is returned.
*/
SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
int rc = SQLITE_OK; /* Return code */
/* This routine is only called from b-tree and only when there are no
** outstanding pages. This implies that the pager state should either
** be OPEN or READER. READER is only possible if the pager is or was in
** exclusive access mode.
*/
assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
assert( assert_pager_state(pPager) );
assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; }
if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){
int bHotJournal = 1; /* True if there exists a hot journal-file */
assert( !MEMDB );
assert( pPager->noReadlock==0 || pPager->readOnly );
if( pPager->noReadlock==0 ){
rc = pager_wait_on_lock(pPager, SHARED_LOCK);
if( rc!=SQLITE_OK ){
assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
goto failed;
}
}
/* If a journal file exists, and there is no RESERVED lock on the
** database file, then it either needs to be played back or deleted.
*/
if( pPager->eLock<=SHARED_LOCK ){
rc = hasHotJournal(pPager, &bHotJournal);
}
if( rc!=SQLITE_OK ){
goto failed;
}
if( bHotJournal ){
/* Get an EXCLUSIVE lock on the database file. At this point it is
** important that a RESERVED lock is not obtained on the way to the
** EXCLUSIVE lock. If it were, another process might open the
** database file, detect the RESERVED lock, and conclude that the
** database is safe to read while this process is still rolling the
** hot-journal back.
**
** Because the intermediate RESERVED lock is not requested, any
** other process attempting to access the database file will get to
** this point in the code and fail to obtain its own EXCLUSIVE lock
** on the database file.
**
** Unless the pager is in locking_mode=exclusive mode, the lock is
** downgraded to SHARED_LOCK before this function returns.
*/
rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
if( rc!=SQLITE_OK ){
goto failed;
}
/* If it is not already open and the file exists on disk, open the
** journal for read/write access. Write access is required because
** in exclusive-access mode the file descriptor will be kept open
** and possibly used for a transaction later on. Also, write-access
** is usually required to finalize the journal in journal_mode=persist
** mode (and also for journal_mode=truncate on some systems).
**
** If the journal does not exist, it usually means that some
** other connection managed to get in and roll it back before
** this connection obtained the exclusive lock above. Or, it
** may mean that the pager was in the error-state when this
** function was called and the journal file does not exist.
*/
if( !isOpen(pPager->jfd) ){
sqlite3_vfs * const pVfs = pPager->pVfs;
int bExists; /* True if journal file exists */
rc = sqlite3OsAccess(
pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &bExists);
if( rc==SQLITE_OK && bExists ){
int fout = 0;
int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
assert( !pPager->tempFile );
rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout);
assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){
rc = SQLITE_CANTOPEN_BKPT;
sqlite3OsClose(pPager->jfd);
}
}
}
/* Playback and delete the journal. Drop the database write
** lock and reacquire the read lock. Purge the cache before
** playing back the hot-journal so that we don't end up with
** an inconsistent cache. Sync the hot journal before playing
** it back since the process that crashed and left the hot journal
** probably did not sync it and we are required to always sync
** the journal before playing it back.
*/
if( isOpen(pPager->jfd) ){
assert( rc==SQLITE_OK );
rc = pagerSyncHotJournal(pPager);
if( rc==SQLITE_OK ){
rc = pager_playback(pPager, 1);
pPager->eState = PAGER_OPEN;
}
}else if( !pPager->exclusiveMode ){
pagerUnlockDb(pPager, SHARED_LOCK);
}
if( rc!=SQLITE_OK ){
/* This branch is taken if an error occurs while trying to open
** or roll back a hot-journal while holding an EXCLUSIVE lock. The
** pager_unlock() routine will be called before returning to unlock
** the file. If the unlock attempt fails, then Pager.eLock must be
** set to UNKNOWN_LOCK (see the comment above the #define for
** UNKNOWN_LOCK above for an explanation).
**
** In order to get pager_unlock() to do this, set Pager.eState to
** PAGER_ERROR now. This is not actually counted as a transition
** to ERROR state in the state diagram at the top of this file,
** since we know that the same call to pager_unlock() will very
** shortly transition the pager object to the OPEN state. Calling
** assert_pager_state() would fail now, as it should not be possible
** to be in ERROR state when there are zero outstanding page
** references.
*/
pager_error(pPager, rc);
goto failed;
}
assert( pPager->eState==PAGER_OPEN );
assert( (pPager->eLock==SHARED_LOCK)
|| (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK)
);
}
if( !pPager->tempFile
&& (pPager->pBackup || sqlite3PcachePagecount(pPager->pPCache)>0)
){
/* The shared-lock has just been acquired on the database file
** and there are already pages in the cache (from a previous
** read or write transaction). Check to see if the database
** has been modified. If the database has changed, flush the
** cache.
**
** Database changes is detected by looking at 15 bytes beginning
** at offset 24 into the file. The first 4 of these 16 bytes are
** a 32-bit counter that is incremented with each change. The
** other bytes change randomly with each file change when
** a codec is in use.
**
** There is a vanishingly small chance that a change will not be
** detected. The chance of an undetected change is so small that
** it can be neglected.
*/
Pgno nPage = 0;
char dbFileVers[sizeof(pPager->dbFileVers)];
rc = pagerPagecount(pPager, &nPage);
if( rc ) goto failed;
if( nPage>0 ){
IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers)));
rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24);
if( rc!=SQLITE_OK ){
goto failed;
}
}else{
memset(dbFileVers, 0, sizeof(dbFileVers));
}
if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
pager_reset(pPager);
}
}
/* If there is a WAL file in the file-system, open this database in WAL
** mode. Otherwise, the following function call is a no-op.
*/
rc = pagerOpenWalIfPresent(pPager);
assert( pPager->pWal==0 || rc==SQLITE_OK );
}
if( pagerUseWal(pPager) ){
assert( rc==SQLITE_OK );
rc = pagerBeginReadTransaction(pPager);
}
if( pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){
rc = pagerPagecount(pPager, &pPager->dbSize);
}
failed:
if( rc!=SQLITE_OK ){
assert( !MEMDB );
pager_unlock(pPager);
assert( pPager->eState==PAGER_OPEN );
}else{
pPager->eState = PAGER_READER;
}
return rc;
}
/*
** If the reference count has reached zero, rollback any active
** transaction and unlock the pager.
**
** Except, in locking_mode=EXCLUSIVE when there is nothing to in
** the rollback journal, the unlock is not performed and there is
** nothing to rollback, so this routine is a no-op.
*/
static void pagerUnlockIfUnused(Pager *pPager){
if( (sqlite3PcacheRefCount(pPager->pPCache)==0) ){
pagerUnlockAndRollback(pPager);
}
}
/*
** Acquire a reference to page number pgno in pager pPager (a page
** reference has type DbPage*). If the requested reference is
|
| ︙ | ︙ | |||
38358 38359 38360 38361 38362 38363 38364 38365 |
Pgno pgno, /* Page number to fetch */
DbPage **ppPage, /* Write a pointer to the page here */
int noContent /* Do not bother reading content from disk if true */
){
int rc;
PgHdr *pPg;
assert( assert_pager_state(pPager) );
| > < | | 39021 39022 39023 39024 39025 39026 39027 39028 39029 39030 39031 39032 39033 39034 39035 39036 39037 39038 39039 39040 39041 39042 39043 39044 |
Pgno pgno, /* Page number to fetch */
DbPage **ppPage, /* Write a pointer to the page here */
int noContent /* Do not bother reading content from disk if true */
){
int rc;
PgHdr *pPg;
assert( pPager->eState>=PAGER_READER );
assert( assert_pager_state(pPager) );
if( pgno==0 ){
return SQLITE_CORRUPT_BKPT;
}
/* If the pager is in the error state, return an error immediately.
** Otherwise, request the page from the PCache layer. */
if( pPager->errCode!=SQLITE_OK ){
rc = pPager->errCode;
}else{
rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, ppPage);
}
if( rc!=SQLITE_OK ){
/* Either the call to sqlite3PcacheFetch() returned an error or the
|
| ︙ | ︙ | |||
38393 38394 38395 38396 38397 38398 38399 |
assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
PAGER_INCR(pPager->nHit);
return SQLITE_OK;
}else{
/* The pager cache has created a new page. Its content needs to
** be initialized. */
| < < < < < < | | 39056 39057 39058 39059 39060 39061 39062 39063 39064 39065 39066 39067 39068 39069 39070 39071 39072 39073 39074 39075 39076 39077 39078 39079 39080 39081 39082 |
assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
PAGER_INCR(pPager->nHit);
return SQLITE_OK;
}else{
/* The pager cache has created a new page. Its content needs to
** be initialized. */
PAGER_INCR(pPager->nMiss);
pPg = *ppPage;
pPg->pPager = pPager;
/* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page
** number greater than this, or the unused locking-page, is requested. */
if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){
rc = SQLITE_CORRUPT_BKPT;
goto pager_acquire_err;
}
if( MEMDB || pPager->dbSize<pgno || noContent || !isOpen(pPager->fd) ){
if( pgno>pPager->mxPgno ){
rc = SQLITE_FULL;
goto pager_acquire_err;
}
if( noContent ){
/* Failure to set the bits in the InJournal bit-vectors is benign.
** It merely means that we might do some extra work to journal a
|
| ︙ | ︙ | |||
38441 38442 38443 38444 38445 38446 38447 |
}else{
assert( pPg->pPager==pPager );
rc = readDbPage(pPg);
if( rc!=SQLITE_OK ){
goto pager_acquire_err;
}
}
| < | < | < < | | 39098 39099 39100 39101 39102 39103 39104 39105 39106 39107 39108 39109 39110 39111 39112 39113 39114 39115 39116 39117 39118 39119 39120 39121 39122 39123 39124 39125 39126 39127 39128 39129 39130 39131 39132 39133 39134 39135 39136 39137 39138 39139 39140 39141 39142 39143 39144 |
}else{
assert( pPg->pPager==pPager );
rc = readDbPage(pPg);
if( rc!=SQLITE_OK ){
goto pager_acquire_err;
}
}
pager_set_pagehash(pPg);
}
return SQLITE_OK;
pager_acquire_err:
assert( rc!=SQLITE_OK );
if( pPg ){
sqlite3PcacheDrop(pPg);
}
pagerUnlockIfUnused(pPager);
*ppPage = 0;
return rc;
}
/*
** Acquire a page if it is already in the in-memory cache. Do
** not read the page from disk. Return a pointer to the page,
** or 0 if the page is not in cache.
**
** See also sqlite3PagerGet(). The difference between this routine
** and sqlite3PagerGet() is that _get() will go to the disk and read
** in the page if the page is not already in cache. This routine
** returns NULL if the page is not in cache or if a disk I/O error
** has ever happened.
*/
SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
PgHdr *pPg = 0;
assert( pPager!=0 );
assert( pgno!=0 );
assert( pPager->pPCache!=0 );
assert( pPager->eState>=PAGER_READER && pPager->eState!=PAGER_ERROR );
sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg);
return pPg;
}
/*
** Release a page reference.
**
|
| ︙ | ︙ | |||
38522 38523 38524 38525 38526 38527 38528 |
**
** Return SQLITE_OK if everything is successful. Otherwise, return
** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or
** an IO error code if opening or writing the journal file fails.
*/
static int pager_open_journal(Pager *pPager){
int rc = SQLITE_OK; /* Return code */
| < < | | | < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < < | | | | | > > > > > < < < < < < < < | > > | < | < < < < < < < < < < < < < < < | | < < | | | | | > > | | > | > | | > > > > > | < < < < < < < < | > > > | | > | > > > > | < < < | | | | | < | < < < < | > | | | > | > > > > > > > > > > | | | < < < | | | | < < < < < < < < < < < | < < < < < < < | < < | 39175 39176 39177 39178 39179 39180 39181 39182 39183 39184 39185 39186 39187 39188 39189 39190 39191 39192 39193 39194 39195 39196 39197 39198 39199 39200 39201 39202 39203 39204 39205 39206 39207 39208 39209 39210 39211 39212 39213 39214 39215 39216 39217 39218 39219 39220 39221 39222 39223 39224 39225 39226 39227 39228 39229 39230 39231 39232 39233 39234 39235 39236 39237 39238 39239 39240 39241 39242 39243 39244 39245 39246 39247 39248 39249 39250 39251 39252 39253 39254 39255 39256 39257 39258 39259 39260 39261 39262 39263 39264 39265 39266 39267 39268 39269 39270 39271 39272 39273 39274 39275 39276 39277 39278 39279 39280 39281 39282 39283 39284 39285 39286 39287 39288 39289 39290 39291 39292 39293 39294 39295 39296 39297 39298 39299 39300 39301 39302 39303 39304 39305 39306 39307 39308 39309 39310 39311 39312 39313 39314 39315 39316 39317 39318 39319 39320 39321 39322 39323 39324 39325 39326 39327 39328 39329 39330 39331 39332 39333 39334 39335 39336 39337 39338 39339 39340 39341 39342 39343 39344 39345 39346 39347 39348 39349 39350 39351 39352 39353 39354 39355 39356 39357 39358 39359 39360 39361 39362 39363 39364 39365 39366 39367 39368 39369 39370 39371 39372 39373 39374 39375 39376 39377 39378 39379 39380 39381 39382 39383 39384 39385 39386 39387 39388 39389 39390 39391 39392 39393 39394 39395 39396 39397 39398 39399 39400 39401 39402 39403 39404 39405 39406 39407 39408 39409 39410 39411 39412 39413 39414 39415 39416 39417 39418 39419 39420 39421 39422 39423 39424 39425 39426 39427 39428 39429 39430 39431 39432 39433 39434 39435 39436 39437 39438 39439 39440 39441 39442 39443 39444 39445 39446 39447 39448 39449 39450 39451 39452 39453 39454 39455 39456 39457 39458 39459 39460 39461 39462 39463 39464 39465 39466 39467 |
**
** Return SQLITE_OK if everything is successful. Otherwise, return
** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or
** an IO error code if opening or writing the journal file fails.
*/
static int pager_open_journal(Pager *pPager){
int rc = SQLITE_OK; /* Return code */
sqlite3_vfs * const pVfs = pPager->pVfs; /* Local cache of vfs pointer */
assert( pPager->eState==PAGER_WRITER_LOCKED );
assert( assert_pager_state(pPager) );
assert( pPager->pInJournal==0 );
/* If already in the error state, this function is a no-op. But on
** the other hand, this routine is never called if we are already in
** an error state. */
if( NEVER(pPager->errCode) ) return pPager->errCode;
if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize);
if( pPager->pInJournal==0 ){
return SQLITE_NOMEM;
}
/* Open the journal file if it is not already open. */
if( !isOpen(pPager->jfd) ){
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
sqlite3MemJournalOpen(pPager->jfd);
}else{
const int flags = /* VFS flags to open journal file */
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
(pPager->tempFile ?
(SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
(SQLITE_OPEN_MAIN_JOURNAL)
);
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
rc = sqlite3JournalOpen(
pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
);
#else
rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
#endif
}
assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
}
/* Write the first journal header to the journal file and open
** the sub-journal if necessary.
*/
if( rc==SQLITE_OK ){
/* TODO: Check if all of these are really required. */
pPager->nRec = 0;
pPager->journalOff = 0;
pPager->setMaster = 0;
pPager->journalHdr = 0;
rc = writeJournalHdr(pPager);
}
}
if( rc!=SQLITE_OK ){
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
}else{
assert( pPager->eState==PAGER_WRITER_LOCKED );
pPager->eState = PAGER_WRITER_CACHEMOD;
}
return rc;
}
/*
** Begin a write-transaction on the specified pager object. If a
** write-transaction has already been opened, this function is a no-op.
**
** If the exFlag argument is false, then acquire at least a RESERVED
** lock on the database file. If exFlag is true, then acquire at least
** an EXCLUSIVE lock. If such a lock is already held, no locking
** functions need be called.
**
** If the subjInMemory argument is non-zero, then any sub-journal opened
** within this transaction will be opened as an in-memory file. This
** has no effect if the sub-journal is already opened (as it may be when
** running in exclusive mode) or if the transaction does not require a
** sub-journal. If the subjInMemory argument is zero, then any required
** sub-journal is implemented in-memory if pPager is an in-memory database,
** or using a temporary file otherwise.
*/
SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
int rc = SQLITE_OK;
if( pPager->errCode ) return pPager->errCode;
assert( pPager->eState>=PAGER_READER && pPager->eState<PAGER_ERROR );
pPager->subjInMemory = (u8)subjInMemory;
if( ALWAYS(pPager->eState==PAGER_READER) ){
assert( pPager->pInJournal==0 );
if( pagerUseWal(pPager) ){
/* If the pager is configured to use locking_mode=exclusive, and an
** exclusive lock on the database is not already held, obtain it now.
*/
if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){
rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
if( rc!=SQLITE_OK ){
return rc;
}
sqlite3WalExclusiveMode(pPager->pWal, 1);
}
/* Grab the write lock on the log file. If successful, upgrade to
** PAGER_RESERVED state. Otherwise, return an error code to the caller.
** The busy-handler is not invoked if another connection already
** holds the write-lock. If possible, the upper layer will call it.
*/
rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
}else{
/* Obtain a RESERVED lock on the database file. If the exFlag parameter
** is true, then immediately upgrade this to an EXCLUSIVE lock. The
** busy-handler callback can be used when upgrading to the EXCLUSIVE
** lock, but not when obtaining the RESERVED lock.
*/
rc = pagerLockDb(pPager, RESERVED_LOCK);
if( rc==SQLITE_OK && exFlag ){
rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
}
}
if( rc==SQLITE_OK ){
/* Change to WRITER_LOCKED state.
**
** WAL mode sets Pager.eState to PAGER_WRITER_LOCKED or CACHEMOD
** when it has an open transaction, but never to DBMOD or FINISHED.
** This is because in those states the code to roll back savepoint
** transactions may copy data from the sub-journal into the database
** file as well as into the page cache. Which would be incorrect in
** WAL mode.
*/
pPager->eState = PAGER_WRITER_LOCKED;
pPager->dbHintSize = pPager->dbSize;
pPager->dbFileSize = pPager->dbSize;
pPager->dbOrigSize = pPager->dbSize;
pPager->journalOff = 0;
}
assert( rc==SQLITE_OK || pPager->eState==PAGER_READER );
assert( rc!=SQLITE_OK || pPager->eState==PAGER_WRITER_LOCKED );
assert( assert_pager_state(pPager) );
}
PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager)));
return rc;
}
/*
** Mark a single data page as writeable. The page is written into the
** main journal or sub-journal as required. If the page is written into
** one of the journals, the corresponding bit is set in the
** Pager.pInJournal bitvec and the PagerSavepoint.pInSavepoint bitvecs
** of any open savepoints as appropriate.
*/
static int pager_write(PgHdr *pPg){
void *pData = pPg->pData;
Pager *pPager = pPg->pPager;
int rc = SQLITE_OK;
/* This routine is not called unless a write-transaction has already
** been started. The journal file may or may not be open at this point.
** It is never called in the ERROR state.
*/
assert( pPager->eState==PAGER_WRITER_LOCKED
|| pPager->eState==PAGER_WRITER_CACHEMOD
|| pPager->eState==PAGER_WRITER_DBMOD
);
assert( assert_pager_state(pPager) );
/* If an error has been previously detected, report the same error
** again. This should not happen, but the check provides robustness. */
if( NEVER(pPager->errCode) ) return pPager->errCode;
/* Higher-level routines never call this function if database is not
** writable. But check anyway, just for robustness. */
if( NEVER(pPager->readOnly) ) return SQLITE_PERM;
CHECK_PAGE(pPg);
/* Mark the page as dirty. If the page has already been written
** to the journal then we can return right away.
*/
sqlite3PcacheMakeDirty(pPg);
if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){
assert( !pagerUseWal(pPager) );
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
}else{
/* If we get this far, it means that the page needs to be
** written to the transaction journal or the checkpoint journal
** or both.
**
** Higher level routines have already obtained the necessary locks
** to begin the write-transaction, but the rollback journal might not
** yet be open. Open it now if this is the case.
*/
if( pPager->eState==PAGER_WRITER_LOCKED ){
rc = pager_open_journal(pPager);
if( rc!=SQLITE_OK ) return rc;
}
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
assert( assert_pager_state(pPager) );
/* The transaction journal now exists and we have a RESERVED or an
** EXCLUSIVE lock on the main database file. Write the current page to
** the transaction journal if it is not there already.
*/
if( !pageInJournal(pPg) && !pagerUseWal(pPager) ){
assert( pagerUseWal(pPager)==0 );
if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){
u32 cksum;
char *pData2;
i64 iOff = pPager->journalOff;
/* We should never write to the journal file the page that
** contains the database locks. The following assert verifies
** that we do not. */
assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
assert( pPager->journalHdr<=pPager->journalOff );
CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
cksum = pager_cksum(pPager, (u8*)pData2);
/* Even if an IO or diskfull error occurs while journalling the
** page in the block above, set the need-sync flag for the page.
** Otherwise, when the transaction is rolled back, the logic in
** playback_one_page() will think that the page needs to be restored
** in the database file. And if an IO error occurs while doing so,
** then corruption may follow.
*/
pPg->flags |= PGHDR_NEED_SYNC;
rc = write32bits(pPager->jfd, iOff, pPg->pgno);
if( rc!=SQLITE_OK ) return rc;
rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4);
if( rc!=SQLITE_OK ) return rc;
rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum);
if( rc!=SQLITE_OK ) return rc;
IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
pPager->journalOff, pPager->pageSize));
PAGER_INCR(sqlite3_pager_writej_count);
PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n",
PAGERID(pPager), pPg->pgno,
((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg)));
pPager->journalOff += 8 + pPager->pageSize;
pPager->nRec++;
assert( pPager->pInJournal!=0 );
rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
testcase( rc==SQLITE_NOMEM );
assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
rc |= addToSavepointBitvecs(pPager, pPg->pgno);
if( rc!=SQLITE_OK ){
assert( rc==SQLITE_NOMEM );
return rc;
}
}else{
if( pPager->eState!=PAGER_WRITER_DBMOD ){
pPg->flags |= PGHDR_NEED_SYNC;
}
PAGERTRACE(("APPEND %d page %d needSync=%d\n",
PAGERID(pPager), pPg->pgno,
((pPg->flags&PGHDR_NEED_SYNC)?1:0)));
}
}
/* If the statement journal is open and the page is not in it,
** then write the current page to the statement journal. Note that
** the statement journal format differs from the standard journal format
** in that it omits the checksums and the header.
*/
if( subjRequiresPage(pPg) ){
rc = subjournalPage(pPg);
}
}
/* Update the database size and return.
*/
if( pPager->dbSize<pPg->pgno ){
pPager->dbSize = pPg->pgno;
}
return rc;
}
/*
|
| ︙ | ︙ | |||
38864 38865 38866 38867 38868 38869 38870 38871 38872 38873 38874 38875 38876 38877 |
SQLITE_PRIVATE int sqlite3PagerWrite(DbPage *pDbPage){
int rc = SQLITE_OK;
PgHdr *pPg = pDbPage;
Pager *pPager = pPg->pPager;
Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
if( nPagePerSector>1 ){
Pgno nPageCount; /* Total number of pages in database file */
Pgno pg1; /* First page of the sector pPg is located on. */
int nPage = 0; /* Number of pages starting at pg1 to journal */
int ii; /* Loop counter */
int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */
| > > > > | 39481 39482 39483 39484 39485 39486 39487 39488 39489 39490 39491 39492 39493 39494 39495 39496 39497 39498 |
SQLITE_PRIVATE int sqlite3PagerWrite(DbPage *pDbPage){
int rc = SQLITE_OK;
PgHdr *pPg = pDbPage;
Pager *pPager = pPg->pPager;
Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
assert( pPager->eState>=PAGER_WRITER_LOCKED );
assert( pPager->eState!=PAGER_ERROR );
assert( assert_pager_state(pPager) );
if( nPagePerSector>1 ){
Pgno nPageCount; /* Total number of pages in database file */
Pgno pg1; /* First page of the sector pPg is located on. */
int nPage = 0; /* Number of pages starting at pg1 to journal */
int ii; /* Loop counter */
int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */
|
| ︙ | ︙ | |||
38885 38886 38887 38888 38889 38890 38891 |
/* This trick assumes that both the page-size and sector-size are
** an integer power of 2. It sets variable pg1 to the identifier
** of the first page of the sector pPg is located on.
*/
pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1;
| | < | | | | | | | | | | < < | < | 39506 39507 39508 39509 39510 39511 39512 39513 39514 39515 39516 39517 39518 39519 39520 39521 39522 39523 39524 39525 39526 39527 39528 39529 39530 39531 39532 39533 39534 39535 39536 39537 39538 39539 39540 39541 39542 39543 39544 39545 39546 39547 39548 39549 39550 39551 39552 39553 39554 39555 39556 39557 39558 39559 39560 39561 39562 39563 39564 39565 39566 39567 39568 |
/* This trick assumes that both the page-size and sector-size are
** an integer power of 2. It sets variable pg1 to the identifier
** of the first page of the sector pPg is located on.
*/
pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1;
nPageCount = pPager->dbSize;
if( pPg->pgno>nPageCount ){
nPage = (pPg->pgno - pg1)+1;
}else if( (pg1+nPagePerSector-1)>nPageCount ){
nPage = nPageCount+1-pg1;
}else{
nPage = nPagePerSector;
}
assert(nPage>0);
assert(pg1<=pPg->pgno);
assert((pg1+nPage)>pPg->pgno);
for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){
Pgno pg = pg1+ii;
PgHdr *pPage;
if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){
if( pg!=PAGER_MJ_PGNO(pPager) ){
rc = sqlite3PagerGet(pPager, pg, &pPage);
if( rc==SQLITE_OK ){
rc = pager_write(pPage);
if( pPage->flags&PGHDR_NEED_SYNC ){
needSync = 1;
}
sqlite3PagerUnref(pPage);
}
}
}else if( (pPage = pager_lookup(pPager, pg))!=0 ){
if( pPage->flags&PGHDR_NEED_SYNC ){
needSync = 1;
}
sqlite3PagerUnref(pPage);
}
}
/* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages
** starting at pg1, then it needs to be set for all of them. Because
** writing to any of these nPage pages may damage the others, the
** journal file must contain sync()ed copies of all of them
** before any of them can be written out to the database file.
*/
if( rc==SQLITE_OK && needSync ){
assert( !MEMDB );
for(ii=0; ii<nPage; ii++){
PgHdr *pPage = pager_lookup(pPager, pg1+ii);
if( pPage ){
pPage->flags |= PGHDR_NEED_SYNC;
sqlite3PagerUnref(pPage);
}
}
}
assert( pPager->doNotSyncSpill==1 );
pPager->doNotSyncSpill--;
}else{
rc = pager_write(pDbPage);
}
|
| ︙ | ︙ | |||
38979 38980 38981 38982 38983 38984 38985 |
*/
SQLITE_PRIVATE void sqlite3PagerDontWrite(PgHdr *pPg){
Pager *pPager = pPg->pPager;
if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager)));
IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno))
pPg->flags |= PGHDR_DONT_WRITE;
| < | < | 39596 39597 39598 39599 39600 39601 39602 39603 39604 39605 39606 39607 39608 39609 39610 |
*/
SQLITE_PRIVATE void sqlite3PagerDontWrite(PgHdr *pPg){
Pager *pPager = pPg->pPager;
if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager)));
IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno))
pPg->flags |= PGHDR_DONT_WRITE;
pager_set_pagehash(pPg);
}
}
/*
** This routine is called to increment the value of the database file
** change-counter, stored as a 4-byte big-endian integer starting at
** byte offset 24 of the pager file.
|
| ︙ | ︙ | |||
39003 39004 39005 39006 39007 39008 39009 39010 39011 39012 39013 39014 39015 39016 39017 39018 39019 39020 39021 39022 39023 39024 39025 39026 39027 39028 |
** with the SQLITE_ENABLE_ATOMIC_WRITE macro defined. In this case,
** if isDirect is non-zero, then the database file is updated directly
** by writing an updated version of page 1 using a call to the
** sqlite3OsWrite() function.
*/
static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
int rc = SQLITE_OK;
/* Declare and initialize constant integer 'isDirect'. If the
** atomic-write optimization is enabled in this build, then isDirect
** is initialized to the value passed as the isDirectMode parameter
** to this function. Otherwise, it is always set to zero.
**
** The idea is that if the atomic-write optimization is not
** enabled at compile time, the compiler can omit the tests of
** 'isDirect' below, as well as the block enclosed in the
** "if( isDirect )" condition.
*/
#ifndef SQLITE_ENABLE_ATOMIC_WRITE
# define DIRECT_MODE 0
assert( isDirectMode==0 );
UNUSED_PARAMETER(isDirectMode);
#else
# define DIRECT_MODE isDirectMode
#endif
| > > > > > < | 39618 39619 39620 39621 39622 39623 39624 39625 39626 39627 39628 39629 39630 39631 39632 39633 39634 39635 39636 39637 39638 39639 39640 39641 39642 39643 39644 39645 39646 39647 39648 39649 39650 39651 39652 39653 39654 39655 |
** with the SQLITE_ENABLE_ATOMIC_WRITE macro defined. In this case,
** if isDirect is non-zero, then the database file is updated directly
** by writing an updated version of page 1 using a call to the
** sqlite3OsWrite() function.
*/
static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
int rc = SQLITE_OK;
assert( pPager->eState==PAGER_WRITER_CACHEMOD
|| pPager->eState==PAGER_WRITER_DBMOD
);
assert( assert_pager_state(pPager) );
/* Declare and initialize constant integer 'isDirect'. If the
** atomic-write optimization is enabled in this build, then isDirect
** is initialized to the value passed as the isDirectMode parameter
** to this function. Otherwise, it is always set to zero.
**
** The idea is that if the atomic-write optimization is not
** enabled at compile time, the compiler can omit the tests of
** 'isDirect' below, as well as the block enclosed in the
** "if( isDirect )" condition.
*/
#ifndef SQLITE_ENABLE_ATOMIC_WRITE
# define DIRECT_MODE 0
assert( isDirectMode==0 );
UNUSED_PARAMETER(isDirectMode);
#else
# define DIRECT_MODE isDirectMode
#endif
if( !pPager->changeCountDone && pPager->dbSize>0 ){
PgHdr *pPgHdr; /* Reference to page 1 */
u32 change_counter; /* Initial value of change-counter field */
assert( !pPager->tempFile && isOpen(pPager->fd) );
/* Open page 1 of the file for writing. */
|
| ︙ | ︙ | |||
39107 39108 39109 39110 39111 39112 39113 |
** If the EXCLUSIVE lock is already held or the attempt to obtain it is
** successful, or the connection is in WAL mode, SQLITE_OK is returned.
** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is
** returned.
*/
SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager){
int rc = SQLITE_OK;
| | > > > > | | 39726 39727 39728 39729 39730 39731 39732 39733 39734 39735 39736 39737 39738 39739 39740 39741 39742 39743 39744 39745 39746 |
** If the EXCLUSIVE lock is already held or the attempt to obtain it is
** successful, or the connection is in WAL mode, SQLITE_OK is returned.
** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is
** returned.
*/
SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager){
int rc = SQLITE_OK;
assert( pPager->eState==PAGER_WRITER_CACHEMOD
|| pPager->eState==PAGER_WRITER_DBMOD
|| pPager->eState==PAGER_WRITER_LOCKED
);
assert( assert_pager_state(pPager) );
if( 0==pagerUseWal(pPager) ){
rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
}
return rc;
}
/*
** Sync the database file for the pager pPager. zMaster points to the name
** of a master journal file that should be written into the individual
|
| ︙ | ︙ | |||
39147 39148 39149 39150 39151 39152 39153 |
SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
Pager *pPager, /* Pager object */
const char *zMaster, /* If not NULL, the master journal name */
int noSync /* True to omit the xSync on the db file */
){
int rc = SQLITE_OK; /* Return code */
| < | > > > > > | > > > | | | 39770 39771 39772 39773 39774 39775 39776 39777 39778 39779 39780 39781 39782 39783 39784 39785 39786 39787 39788 39789 39790 39791 39792 39793 39794 39795 39796 39797 39798 39799 39800 39801 39802 39803 39804 39805 39806 |
SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
Pager *pPager, /* Pager object */
const char *zMaster, /* If not NULL, the master journal name */
int noSync /* True to omit the xSync on the db file */
){
int rc = SQLITE_OK; /* Return code */
assert( pPager->eState==PAGER_WRITER_LOCKED
|| pPager->eState==PAGER_WRITER_CACHEMOD
|| pPager->eState==PAGER_WRITER_DBMOD
|| pPager->eState==PAGER_ERROR
);
assert( assert_pager_state(pPager) );
/* If a prior error occurred, report that error again. */
if( NEVER(pPager->errCode) ) return pPager->errCode;
PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n",
pPager->zFilename, zMaster, pPager->dbSize));
/* If no database changes have been made, return early. */
if( pPager->eState<PAGER_WRITER_CACHEMOD ) return SQLITE_OK;
if( MEMDB ){
/* If this is an in-memory db, or no pages have been written to, or this
** function has already been called, it is mostly a no-op. However, any
** backup in progress needs to be restarted.
*/
sqlite3BackupRestart(pPager->pBackup);
}else{
if( pagerUseWal(pPager) ){
PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
if( pList ){
rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1,
(pPager->fullSync ? pPager->sync_flags : 0)
);
}
|
| ︙ | ︙ | |||
39205 39206 39207 39208 39209 39210 39211 |
PgHdr *pPg;
assert( isOpen(pPager->jfd)
|| pPager->journalMode==PAGER_JOURNALMODE_OFF
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
);
if( !zMaster && isOpen(pPager->jfd)
&& pPager->journalOff==jrnlBufferSize(pPager)
| | | 39835 39836 39837 39838 39839 39840 39841 39842 39843 39844 39845 39846 39847 39848 39849 |
PgHdr *pPg;
assert( isOpen(pPager->jfd)
|| pPager->journalMode==PAGER_JOURNALMODE_OFF
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
);
if( !zMaster && isOpen(pPager->jfd)
&& pPager->journalOff==jrnlBufferSize(pPager)
&& pPager->dbSize>=pPager->dbOrigSize
&& (0==(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty)
){
/* Update the db file change counter via the direct-write method. The
** following call will modify the in-memory representation of page 1
** to include the updated change counter and then write page 1
** directly to the database file. Because of the atomic-write
** property of the host file-system, this is safe.
|
| ︙ | ︙ | |||
39235 39236 39237 39238 39239 39240 39241 |
** file. This can only happen in auto-vacuum mode.
**
** Before reading the pages with page numbers larger than the
** current value of Pager.dbSize, set dbSize back to the value
** that it took at the start of the transaction. Otherwise, the
** calls to sqlite3PagerGet() return zeroed pages instead of
** reading data from the database file.
| < < < | | 39865 39866 39867 39868 39869 39870 39871 39872 39873 39874 39875 39876 39877 39878 39879 39880 39881 39882 |
** file. This can only happen in auto-vacuum mode.
**
** Before reading the pages with page numbers larger than the
** current value of Pager.dbSize, set dbSize back to the value
** that it took at the start of the transaction. Otherwise, the
** calls to sqlite3PagerGet() return zeroed pages instead of
** reading data from the database file.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pPager->dbSize<pPager->dbOrigSize
&& pPager->journalMode!=PAGER_JOURNALMODE_OFF
){
Pgno i; /* Iterator variable */
const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */
const Pgno dbSize = pPager->dbSize; /* Database image size */
pPager->dbSize = pPager->dbOrigSize;
for( i=dbSize+1; i<=pPager->dbOrigSize; i++ ){
if( !sqlite3BitvecTest(pPager->pInJournal, i) && i!=iSkip ){
|
| ︙ | ︙ | |||
39268 39269 39270 39271 39272 39273 39274 |
/* Write the master journal name into the journal file. If a master
** journal file name has already been written to the journal file,
** or if zMaster is NULL (no master journal), then this call is a no-op.
*/
rc = writeMasterJournal(pPager, zMaster);
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
| > | | | > > > > > > | < | < < < > > > | 39895 39896 39897 39898 39899 39900 39901 39902 39903 39904 39905 39906 39907 39908 39909 39910 39911 39912 39913 39914 39915 39916 39917 39918 39919 39920 39921 39922 39923 39924 39925 39926 39927 39928 39929 39930 39931 39932 39933 39934 39935 39936 39937 39938 39939 39940 39941 39942 39943 39944 39945 39946 39947 39948 39949 39950 39951 |
/* Write the master journal name into the journal file. If a master
** journal file name has already been written to the journal file,
** or if zMaster is NULL (no master journal), then this call is a no-op.
*/
rc = writeMasterJournal(pPager, zMaster);
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
/* Sync the journal file and write all dirty pages to the database.
** If the atomic-update optimization is being used, this sync will not
** create the journal file or perform any real IO.
**
** Because the change-counter page was just modified, unless the
** atomic-update optimization is used it is almost certain that the
** journal requires a sync here. However, in locking_mode=exclusive
** on a system under memory pressure it is just possible that this is
** not the case. In this case it is likely enough that the redundant
** xSync() call will be changed to a no-op by the OS anyhow.
*/
rc = syncJournal(pPager, 0);
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
rc = pager_write_pagelist(pPager,sqlite3PcacheDirtyList(pPager->pPCache));
if( rc!=SQLITE_OK ){
assert( rc!=SQLITE_IOERR_BLOCKED );
goto commit_phase_one_exit;
}
sqlite3PcacheCleanAll(pPager->pPCache);
/* If the file on disk is not the same size as the database image,
** then use pager_truncate to grow or shrink the file here.
*/
if( pPager->dbSize!=pPager->dbFileSize ){
Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager));
assert( pPager->eState==PAGER_WRITER_DBMOD );
rc = pager_truncate(pPager, nNew);
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
}
/* Finally, sync the database file. */
if( !pPager->noSync && !noSync ){
rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
}
IOTRACE(("DBSYNC %p\n", pPager))
}
}
commit_phase_one_exit:
if( rc==SQLITE_OK && !pagerUseWal(pPager) ){
pPager->eState = PAGER_WRITER_FINISHED;
}
return rc;
}
/*
** When this function is called, the database file has been completely
** updated to reflect the changes made by the current transaction and
|
| ︙ | ︙ | |||
39332 39333 39334 39335 39336 39337 39338 | int rc = SQLITE_OK; /* Return code */ /* This routine should not be called if a prior error has occurred. ** But if (due to a coding error elsewhere in the system) it does get ** called, just return the same error code without doing anything. */ if( NEVER(pPager->errCode) ) return pPager->errCode; | | | | < < > | > | > < > > | > > > > | > < < | < < < < < < < < < | < < < < < < < < < > | < < < < < < < > > > > > > > > > > > < < | | < < < < < < < | < < < | < < | | < < > | < | | < | | < < | 39965 39966 39967 39968 39969 39970 39971 39972 39973 39974 39975 39976 39977 39978 39979 39980 39981 39982 39983 39984 39985 39986 39987 39988 39989 39990 39991 39992 39993 39994 39995 39996 39997 39998 39999 40000 40001 40002 40003 40004 40005 40006 40007 40008 40009 40010 40011 40012 40013 40014 40015 40016 40017 40018 40019 40020 40021 40022 40023 40024 40025 40026 40027 40028 40029 40030 40031 40032 40033 40034 40035 40036 40037 40038 40039 40040 40041 40042 40043 40044 40045 40046 40047 40048 40049 40050 40051 40052 40053 40054 40055 40056 40057 40058 40059 40060 40061 40062 40063 40064 40065 |
int rc = SQLITE_OK; /* Return code */
/* This routine should not be called if a prior error has occurred.
** But if (due to a coding error elsewhere in the system) it does get
** called, just return the same error code without doing anything. */
if( NEVER(pPager->errCode) ) return pPager->errCode;
assert( pPager->eState==PAGER_WRITER_LOCKED
|| pPager->eState==PAGER_WRITER_FINISHED
|| (pagerUseWal(pPager) && pPager->eState==PAGER_WRITER_CACHEMOD)
);
assert( assert_pager_state(pPager) );
/* An optimization. If the database was not actually modified during
** this transaction, the pager is running in exclusive-mode and is
** using persistent journals, then this function is a no-op.
**
** The start of the journal file currently contains a single journal
** header with the nRec field set to 0. If such a journal is used as
** a hot-journal during hot-journal rollback, 0 changes will be made
** to the database file. So there is no need to zero the journal
** header. Since the pager is in exclusive mode, there is no need
** to drop any locks either.
*/
if( pPager->eState==PAGER_WRITER_LOCKED
&& pPager->exclusiveMode
&& pPager->journalMode==PAGER_JOURNALMODE_PERSIST
){
assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) || !pPager->journalOff );
pPager->eState = PAGER_READER;
return SQLITE_OK;
}
PAGERTRACE(("COMMIT %d\n", PAGERID(pPager)));
rc = pager_end_transaction(pPager, pPager->setMaster);
return pager_error(pPager, rc);
}
/*
** If a write transaction is open, then all changes made within the
** transaction are reverted and the current write-transaction is closed.
** The pager falls back to PAGER_READER state if successful, or PAGER_ERROR
** state if an error occurs.
**
** If the pager is already in PAGER_ERROR state when this function is called,
** it returns Pager.errCode immediately. No work is performed in this case.
**
** Otherwise, in rollback mode, this function performs two functions:
**
** 1) It rolls back the journal file, restoring all database file and
** in-memory cache pages to the state they were in when the transaction
** was opened, and
**
** 2) It finalizes the journal file, so that it is not used for hot
** rollback at any point in the future.
**
** Finalization of the journal file (task 2) is only performed if the
** rollback is successful.
**
** In WAL mode, all cache-entries containing data modified within the
** current transaction are either expelled from the cache or reverted to
** their pre-transaction state by re-reading data from the database or
** WAL files. The WAL transaction is then closed.
*/
SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){
int rc = SQLITE_OK; /* Return code */
PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager)));
/* PagerRollback() is a no-op if called in READER or OPEN state. If
** the pager is already in the ERROR state, the rollback is not
** attempted here. Instead, the error code is returned to the caller.
*/
assert( assert_pager_state(pPager) );
if( pPager->eState==PAGER_ERROR ) return pPager->errCode;
if( pPager->eState<=PAGER_READER ) return SQLITE_OK;
if( pagerUseWal(pPager) ){
int rc2;
rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1);
rc2 = pager_end_transaction(pPager, pPager->setMaster);
if( rc==SQLITE_OK ) rc = rc2;
}else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){
rc = pager_end_transaction(pPager, 0);
}else{
rc = pager_playback(pPager, 0);
}
assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK );
assert( rc==SQLITE_OK || rc==SQLITE_FULL || (rc&0xFF)==SQLITE_IOERR );
/* If an error occurs during a ROLLBACK, we can no longer trust the pager
** cache. So call pager_error() on the way out to make any error persistent.
*/
return pager_error(pPager, rc);
}
/*
** Return TRUE if the database file is opened read-only. Return FALSE
** if the database is (in theory) writable.
*/
SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager *pPager){
|
| ︙ | ︙ | |||
39491 39492 39493 39494 39495 39496 39497 |
** This routine is used for testing and analysis only.
*/
SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
static int a[11];
a[0] = sqlite3PcacheRefCount(pPager->pPCache);
a[1] = sqlite3PcachePagecount(pPager->pPCache);
a[2] = sqlite3PcacheGetCachesize(pPager->pPCache);
| | | | 40097 40098 40099 40100 40101 40102 40103 40104 40105 40106 40107 40108 40109 40110 40111 40112 |
** This routine is used for testing and analysis only.
*/
SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
static int a[11];
a[0] = sqlite3PcacheRefCount(pPager->pPCache);
a[1] = sqlite3PcachePagecount(pPager->pPCache);
a[2] = sqlite3PcacheGetCachesize(pPager->pPCache);
a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
a[4] = pPager->eState;
a[5] = pPager->errCode;
a[6] = pPager->nHit;
a[7] = pPager->nMiss;
a[8] = 0; /* Used to be pPager->nOvfl */
a[9] = pPager->nRead;
a[10] = pPager->nWrite;
return a;
|
| ︙ | ︙ | |||
39523 39524 39525 39526 39527 39528 39529 39530 39531 39532 39533 |
** If a memory allocation fails, SQLITE_NOMEM is returned. If an error
** occurs while opening the sub-journal file, then an IO error code is
** returned. Otherwise, SQLITE_OK.
*/
SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
int rc = SQLITE_OK; /* Return code */
int nCurrent = pPager->nSavepoint; /* Current number of savepoints */
if( nSavepoint>nCurrent && pPager->useJournal ){
int ii; /* Iterator variable */
PagerSavepoint *aNew; /* New Pager.aSavepoint array */
| > > > < < < < | | | 40129 40130 40131 40132 40133 40134 40135 40136 40137 40138 40139 40140 40141 40142 40143 40144 40145 40146 40147 40148 40149 40150 40151 40152 40153 40154 40155 40156 40157 40158 40159 40160 40161 40162 40163 40164 40165 40166 40167 40168 40169 40170 40171 40172 40173 |
** If a memory allocation fails, SQLITE_NOMEM is returned. If an error
** occurs while opening the sub-journal file, then an IO error code is
** returned. Otherwise, SQLITE_OK.
*/
SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
int rc = SQLITE_OK; /* Return code */
int nCurrent = pPager->nSavepoint; /* Current number of savepoints */
assert( pPager->eState>=PAGER_WRITER_LOCKED );
assert( assert_pager_state(pPager) );
if( nSavepoint>nCurrent && pPager->useJournal ){
int ii; /* Iterator variable */
PagerSavepoint *aNew; /* New Pager.aSavepoint array */
/* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
** if the allocation fails. Otherwise, zero the new portion in case a
** malloc failure occurs while populating it in the for(...) loop below.
*/
aNew = (PagerSavepoint *)sqlite3Realloc(
pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint
);
if( !aNew ){
return SQLITE_NOMEM;
}
memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
pPager->aSavepoint = aNew;
/* Populate the PagerSavepoint structures just allocated. */
for(ii=nCurrent; ii<nSavepoint; ii++){
aNew[ii].nOrig = pPager->dbSize;
if( isOpen(pPager->jfd) && pPager->journalOff>0 ){
aNew[ii].iOffset = pPager->journalOff;
}else{
aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
}
aNew[ii].iSubRec = pPager->nSubRec;
aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
if( !aNew[ii].pInSavepoint ){
return SQLITE_NOMEM;
}
if( pagerUseWal(pPager) ){
sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
}
pPager->nSavepoint = ii+1;
|
| ︙ | ︙ | |||
39601 39602 39603 39604 39605 39606 39607 |
** then savepoint iSavepoint is also destroyed.
**
** This function may return SQLITE_NOMEM if a memory allocation fails,
** or an IO error code if an IO error occurs while rolling back a
** savepoint. If no errors occur, SQLITE_OK is returned.
*/
SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
| | | | 40206 40207 40208 40209 40210 40211 40212 40213 40214 40215 40216 40217 40218 40219 40220 40221 40222 40223 40224 40225 |
** then savepoint iSavepoint is also destroyed.
**
** This function may return SQLITE_NOMEM if a memory allocation fails,
** or an IO error code if an IO error occurs while rolling back a
** savepoint. If no errors occur, SQLITE_OK is returned.
*/
SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
int rc = pPager->errCode; /* Return code */
assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK );
if( rc==SQLITE_OK && iSavepoint<pPager->nSavepoint ){
int ii; /* Iterator variable */
int nNew; /* Number of remaining savepoints after this op. */
/* Figure out how many savepoints will still be active after this
** operation. Store this value in nNew. Then free resources associated
** with any savepoints that are destroyed by this operation.
*/
|
| ︙ | ︙ | |||
39642 39643 39644 39645 39646 39647 39648 |
** the database file, so the playback operation can be skipped.
*/
else if( pagerUseWal(pPager) || isOpen(pPager->jfd) ){
PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1];
rc = pagerPlaybackSavepoint(pPager, pSavepoint);
assert(rc!=SQLITE_DONE);
}
| | | | 40247 40248 40249 40250 40251 40252 40253 40254 40255 40256 40257 40258 40259 40260 40261 40262 |
** the database file, so the playback operation can be skipped.
*/
else if( pagerUseWal(pPager) || isOpen(pPager->jfd) ){
PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1];
rc = pagerPlaybackSavepoint(pPager, pSavepoint);
assert(rc!=SQLITE_DONE);
}
}
return rc;
}
/*
** Return the full pathname of the database file.
*/
SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager *pPager){
|
| ︙ | ︙ | |||
39741 39742 39743 39744 39745 39746 39747 39748 39749 39750 39751 39752 39753 39754 |
SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
PgHdr *pPgOld; /* The page being overwritten. */
Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */
int rc; /* Return code */
Pgno origPgno; /* The original page number */
assert( pPg->nRef>0 );
/* In order to be able to rollback, an in-memory database must journal
** the page we are moving from.
*/
if( MEMDB ){
rc = sqlite3PagerWrite(pPg);
if( rc ) return rc;
| > > > > | 40346 40347 40348 40349 40350 40351 40352 40353 40354 40355 40356 40357 40358 40359 40360 40361 40362 40363 |
SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
PgHdr *pPgOld; /* The page being overwritten. */
Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */
int rc; /* Return code */
Pgno origPgno; /* The original page number */
assert( pPg->nRef>0 );
assert( pPager->eState==PAGER_WRITER_CACHEMOD
|| pPager->eState==PAGER_WRITER_DBMOD
);
assert( assert_pager_state(pPager) );
/* In order to be able to rollback, an in-memory database must journal
** the page we are moving from.
*/
if( MEMDB ){
rc = sqlite3PagerWrite(pPg);
if( rc ) return rc;
|
| ︙ | ︙ | |||
39790 39791 39792 39793 39794 39795 39796 |
** the journal needs to be sync()ed before database page pPg->pgno
** can be written to. The caller has already promised not to write to it.
*/
if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){
needSyncPgno = pPg->pgno;
assert( pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize );
assert( pPg->flags&PGHDR_DIRTY );
| < | < | > > > > > > > > > | < < < < < < < < < < < < < < < < | 40399 40400 40401 40402 40403 40404 40405 40406 40407 40408 40409 40410 40411 40412 40413 40414 40415 40416 40417 40418 40419 40420 40421 40422 40423 40424 40425 40426 40427 40428 40429 40430 40431 40432 40433 40434 40435 40436 40437 40438 40439 40440 40441 40442 40443 40444 40445 40446 40447 40448 40449 40450 40451 40452 40453 40454 40455 40456 40457 40458 40459 40460 40461 40462 40463 40464 40465 40466 40467 40468 40469 40470 40471 40472 40473 40474 40475 40476 |
** the journal needs to be sync()ed before database page pPg->pgno
** can be written to. The caller has already promised not to write to it.
*/
if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){
needSyncPgno = pPg->pgno;
assert( pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize );
assert( pPg->flags&PGHDR_DIRTY );
}
/* If the cache contains a page with page-number pgno, remove it
** from its hash chain. Also, if the PGHDR_NEED_SYNC flag was set for
** page pgno before the 'move' operation, it needs to be retained
** for the page moved there.
*/
pPg->flags &= ~PGHDR_NEED_SYNC;
pPgOld = pager_lookup(pPager, pgno);
assert( !pPgOld || pPgOld->nRef==1 );
if( pPgOld ){
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
if( MEMDB ){
/* Do not discard pages from an in-memory database since we might
** need to rollback later. Just move the page out of the way. */
sqlite3PcacheMove(pPgOld, pPager->dbSize+1);
}else{
sqlite3PcacheDrop(pPgOld);
}
}
origPgno = pPg->pgno;
sqlite3PcacheMove(pPg, pgno);
sqlite3PcacheMakeDirty(pPg);
/* For an in-memory database, make sure the original page continues
** to exist, in case the transaction needs to roll back. Use pPgOld
** as the original page since it has already been allocated.
*/
if( MEMDB ){
assert( pPgOld );
sqlite3PcacheMove(pPgOld, origPgno);
sqlite3PagerUnref(pPgOld);
}
if( needSyncPgno ){
/* If needSyncPgno is non-zero, then the journal file needs to be
** sync()ed before any data is written to database file page needSyncPgno.
** Currently, no such page exists in the page-cache and the
** "is journaled" bitvec flag has been set. This needs to be remedied by
** loading the page into the pager-cache and setting the PGHDR_NEED_SYNC
** flag.
**
** If the attempt to load the page into the page-cache fails, (due
** to a malloc() or IO failure), clear the bit in the pInJournal[]
** array. Otherwise, if the page is loaded and written again in
** this transaction, it may be written to the database file before
** it is synced into the journal file. This way, it may end up in
** the journal file twice, but that is not a problem.
*/
PgHdr *pPgHdr;
rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr);
if( rc!=SQLITE_OK ){
if( needSyncPgno<=pPager->dbOrigSize ){
assert( pPager->pTmpSpace!=0 );
sqlite3BitvecClear(pPager->pInJournal, needSyncPgno, pPager->pTmpSpace);
}
return rc;
}
pPgHdr->flags |= PGHDR_NEED_SYNC;
sqlite3PcacheMakeDirty(pPgHdr);
sqlite3PagerUnref(pPgHdr);
}
return SQLITE_OK;
}
#endif
/*
** Return a pointer to the data for the specified page.
*/
|
| ︙ | ︙ | |||
39927 39928 39929 39930 39931 39932 39933 39934 39935 39936 39937 39938 39939 39940 |
**
** * Temporary databases cannot have _WAL journalmode.
**
** The returned indicate the current (possibly updated) journal-mode.
*/
SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
u8 eOld = pPager->journalMode; /* Prior journalmode */
/* The eMode parameter is always valid */
assert( eMode==PAGER_JOURNALMODE_DELETE
|| eMode==PAGER_JOURNALMODE_TRUNCATE
|| eMode==PAGER_JOURNALMODE_PERSIST
|| eMode==PAGER_JOURNALMODE_OFF
|| eMode==PAGER_JOURNALMODE_WAL
| > > > > > > > | 40527 40528 40529 40530 40531 40532 40533 40534 40535 40536 40537 40538 40539 40540 40541 40542 40543 40544 40545 40546 40547 |
**
** * Temporary databases cannot have _WAL journalmode.
**
** The returned indicate the current (possibly updated) journal-mode.
*/
SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
u8 eOld = pPager->journalMode; /* Prior journalmode */
#ifdef SQLITE_DEBUG
/* The print_pager_state() routine is intended to be used by the debugger
** only. We invoke it once here to suppress a compiler warning. */
print_pager_state(pPager);
#endif
/* The eMode parameter is always valid */
assert( eMode==PAGER_JOURNALMODE_DELETE
|| eMode==PAGER_JOURNALMODE_TRUNCATE
|| eMode==PAGER_JOURNALMODE_PERSIST
|| eMode==PAGER_JOURNALMODE_OFF
|| eMode==PAGER_JOURNALMODE_WAL
|
| ︙ | ︙ | |||
39953 39954 39955 39956 39957 39958 39959 |
assert( eOld==PAGER_JOURNALMODE_MEMORY || eOld==PAGER_JOURNALMODE_OFF );
if( eMode!=PAGER_JOURNALMODE_MEMORY && eMode!=PAGER_JOURNALMODE_OFF ){
eMode = eOld;
}
}
if( eMode!=eOld ){
| < < < < < < < | < > | > > > > | | > | | | | | | | | | | | | | | | | > > | | 40560 40561 40562 40563 40564 40565 40566 40567 40568 40569 40570 40571 40572 40573 40574 40575 40576 40577 40578 40579 40580 40581 40582 40583 40584 40585 40586 40587 40588 40589 40590 40591 40592 40593 40594 40595 40596 40597 40598 40599 40600 40601 40602 40603 40604 40605 40606 40607 40608 40609 40610 40611 40612 40613 40614 40615 40616 40617 40618 40619 40620 40621 40622 40623 40624 40625 40626 40627 40628 40629 40630 40631 40632 40633 40634 40635 40636 40637 40638 40639 40640 40641 40642 40643 40644 40645 40646 |
assert( eOld==PAGER_JOURNALMODE_MEMORY || eOld==PAGER_JOURNALMODE_OFF );
if( eMode!=PAGER_JOURNALMODE_MEMORY && eMode!=PAGER_JOURNALMODE_OFF ){
eMode = eOld;
}
}
if( eMode!=eOld ){
/* Change the journal mode. */
assert( pPager->eState!=PAGER_ERROR );
pPager->journalMode = (u8)eMode;
/* When transistioning from TRUNCATE or PERSIST to any other journal
** mode except WAL, unless the pager is in locking_mode=exclusive mode,
** delete the journal file.
*/
assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 );
assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 );
assert( (PAGER_JOURNALMODE_DELETE & 5)==0 );
assert( (PAGER_JOURNALMODE_MEMORY & 5)==4 );
assert( (PAGER_JOURNALMODE_OFF & 5)==0 );
assert( (PAGER_JOURNALMODE_WAL & 5)==5 );
assert( isOpen(pPager->fd) || pPager->exclusiveMode );
if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0 ){
/* In this case we would like to delete the journal file. If it is
** not possible, then that is not a problem. Deleting the journal file
** here is an optimization only.
**
** Before deleting the journal file, obtain a RESERVED lock on the
** database file. This ensures that the journal file is not deleted
** while it is in use by some other client.
*/
sqlite3OsClose(pPager->jfd);
if( pPager->eLock>=RESERVED_LOCK ){
sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
}else{
int rc = SQLITE_OK;
int state = pPager->eState;
assert( state==PAGER_OPEN || state==PAGER_READER );
if( state==PAGER_OPEN ){
rc = sqlite3PagerSharedLock(pPager);
}
if( pPager->eState==PAGER_READER ){
assert( rc==SQLITE_OK );
rc = pagerLockDb(pPager, RESERVED_LOCK);
}
if( rc==SQLITE_OK ){
sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
}
if( rc==SQLITE_OK && state==PAGER_READER ){
pagerUnlockDb(pPager, SHARED_LOCK);
}else if( state==PAGER_OPEN ){
pager_unlock(pPager);
}
assert( state==pPager->eState );
}
}
}
/* Return the new journal mode */
return (int)pPager->journalMode;
}
/*
** Return the current journal mode.
*/
SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager *pPager){
return (int)pPager->journalMode;
}
/*
** Return TRUE if the pager is in a state where it is OK to change the
** journalmode. Journalmode changes can only happen when the database
** is unmodified.
*/
SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager *pPager){
assert( assert_pager_state(pPager) );
if( pPager->eState>=PAGER_WRITER_CACHEMOD ) return 0;
if( NEVER(isOpen(pPager->jfd) && pPager->journalOff>0) ) return 0;
return 1;
}
/*
** Get/set the size-limit used for persistent journal files.
**
|
| ︙ | ︙ | |||
40090 40091 40092 40093 40094 40095 40096 | ** The caller must be holding a SHARED lock on the database file to call ** this function. ** ** If the pager passed as the first argument is open on a real database ** file (not a temp file or an in-memory database), and the WAL file ** is not already open, make an attempt to open it now. If successful, ** return SQLITE_OK. If an error occurs or the VFS used by the pager does | | | | > | > > | > > > > | | 40697 40698 40699 40700 40701 40702 40703 40704 40705 40706 40707 40708 40709 40710 40711 40712 40713 40714 40715 40716 40717 40718 40719 40720 40721 40722 40723 40724 40725 40726 40727 40728 40729 40730 40731 40732 40733 40734 40735 40736 40737 40738 40739 40740 40741 40742 40743 40744 40745 40746 |
** The caller must be holding a SHARED lock on the database file to call
** this function.
**
** If the pager passed as the first argument is open on a real database
** file (not a temp file or an in-memory database), and the WAL file
** is not already open, make an attempt to open it now. If successful,
** return SQLITE_OK. If an error occurs or the VFS used by the pager does
** not support the xShmXXX() methods, return an error code. *pbOpen is
** not modified in either case.
**
** If the pager is open on a temp-file (or in-memory database), or if
** the WAL file is already open, set *pbOpen to 1 and return SQLITE_OK
** without doing anything.
*/
SQLITE_PRIVATE int sqlite3PagerOpenWal(
Pager *pPager, /* Pager object */
int *pbOpen /* OUT: Set to true if call is a no-op */
){
int rc = SQLITE_OK; /* Return code */
assert( assert_pager_state(pPager) );
assert( pPager->eState==PAGER_OPEN || pbOpen );
assert( pPager->eState==PAGER_READER || !pbOpen );
assert( pbOpen==0 || *pbOpen==0 );
assert( pbOpen!=0 || (!pPager->tempFile && !pPager->pWal) );
if( !pPager->tempFile && !pPager->pWal ){
if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN;
/* Close any rollback journal previously open */
sqlite3OsClose(pPager->jfd);
/* Open the connection to the log file. If this operation fails,
** (e.g. due to malloc() failure), unlock the database file and
** return an error code.
*/
rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal);
if( rc==SQLITE_OK ){
pPager->journalMode = PAGER_JOURNALMODE_WAL;
pPager->eState = PAGER_OPEN;
}
}else{
*pbOpen = 1;
}
return rc;
}
/*
** This function is called to close the connection to the log file prior
|
| ︙ | ︙ | |||
40144 40145 40146 40147 40148 40149 40150 |
/* If the log file is not already open, but does exist in the file-system,
** it may need to be checkpointed before the connection can switch to
** rollback mode. Open it now so this can happen.
*/
if( !pPager->pWal ){
int logexists = 0;
| | | | | 40758 40759 40760 40761 40762 40763 40764 40765 40766 40767 40768 40769 40770 40771 40772 40773 40774 40775 40776 40777 40778 40779 40780 40781 40782 40783 40784 40785 40786 40787 40788 40789 40790 40791 40792 40793 40794 40795 40796 40797 40798 |
/* If the log file is not already open, but does exist in the file-system,
** it may need to be checkpointed before the connection can switch to
** rollback mode. Open it now so this can happen.
*/
if( !pPager->pWal ){
int logexists = 0;
rc = pagerLockDb(pPager, SHARED_LOCK);
if( rc==SQLITE_OK ){
rc = sqlite3OsAccess(
pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists
);
}
if( rc==SQLITE_OK && logexists ){
rc = sqlite3WalOpen(pPager->pVfs, pPager->fd,
pPager->zWal, &pPager->pWal);
}
}
/* Checkpoint and close the log. Because an EXCLUSIVE lock is held on
** the database file, the log and log-summary files will be deleted.
*/
if( rc==SQLITE_OK && pPager->pWal ){
rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
if( rc==SQLITE_OK ){
rc = sqlite3WalClose(pPager->pWal,
(pPager->noSync ? 0 : pPager->sync_flags),
pPager->pageSize, (u8*)pPager->pTmpSpace
);
pPager->pWal = 0;
}else{
/* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock
** that we did get back to SHARED. */
pagerUnlockDb(pPager, SQLITE_LOCK_SHARED);
}
}
return rc;
}
#ifdef SQLITE_HAS_CODEC
/*
|
| ︙ | ︙ | |||
40490 40491 40492 40493 40494 40495 40496 40497 40498 40499 40500 40501 40502 40503 |
/*
** The following object holds a copy of the wal-index header content.
**
** The actual header in the wal-index consists of two copies of this
** object.
*/
struct WalIndexHdr {
u32 iVersion; /* Wal-index version */
u32 unused; /* Unused (padding) field */
u32 iChange; /* Counter incremented each transaction */
u8 isInit; /* 1 when initialized */
u8 bigEndCksum; /* True if checksums in WAL are big-endian */
| > > > > | | 41104 41105 41106 41107 41108 41109 41110 41111 41112 41113 41114 41115 41116 41117 41118 41119 41120 41121 41122 41123 41124 41125 41126 41127 41128 41129 |
/*
** The following object holds a copy of the wal-index header content.
**
** The actual header in the wal-index consists of two copies of this
** object.
**
** The szPage value can be any power of 2 between 512 and 32768, inclusive.
** Or it can be 1 to represent a 65536-byte page. The latter case was
** added in 3.7.1 when support for 64K pages was added.
*/
struct WalIndexHdr {
u32 iVersion; /* Wal-index version */
u32 unused; /* Unused (padding) field */
u32 iChange; /* Counter incremented each transaction */
u8 isInit; /* 1 when initialized */
u8 bigEndCksum; /* True if checksums in WAL are big-endian */
u16 szPage; /* Database page size in bytes. 1==64K */
u32 mxFrame; /* Index of last valid frame in the WAL */
u32 nPage; /* Size of database in pages */
u32 aFrameCksum[2]; /* Checksum of last frame in log */
u32 aSalt[2]; /* Two salt values copied from WAL header */
u32 aCksum[2]; /* Checksum over all prior fields */
};
|
| ︙ | ︙ | |||
40608 40609 40610 40611 40612 40613 40614 |
struct Wal {
sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */
sqlite3_file *pDbFd; /* File handle for the database file */
sqlite3_file *pWalFd; /* File handle for WAL file */
u32 iCallback; /* Value to pass to log callback (or 0) */
int nWiData; /* Size of array apWiData */
volatile u32 **apWiData; /* Pointer to wal-index content in memory */
| | | 41226 41227 41228 41229 41230 41231 41232 41233 41234 41235 41236 41237 41238 41239 41240 |
struct Wal {
sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */
sqlite3_file *pDbFd; /* File handle for the database file */
sqlite3_file *pWalFd; /* File handle for WAL file */
u32 iCallback; /* Value to pass to log callback (or 0) */
int nWiData; /* Size of array apWiData */
volatile u32 **apWiData; /* Pointer to wal-index content in memory */
u32 szPage; /* Database page size */
i16 readLock; /* Which read lock is being held. -1 for none */
u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */
u8 writeLock; /* True if in a write transaction */
u8 ckptLock; /* True if holding a checkpoint lock */
u8 readOnly; /* True if the WAL file is open read-only */
WalIndexHdr hdr; /* Wal-index header for current transaction */
const char *zWalName; /* Name of WAL file */
|
| ︙ | ︙ | |||
41279 41280 41281 41282 41283 41284 41285 |
|| szPage&(szPage-1)
|| szPage>SQLITE_MAX_PAGE_SIZE
|| szPage<512
){
goto finished;
}
pWal->hdr.bigEndCksum = (u8)(magic&0x00000001);
| | | 41897 41898 41899 41900 41901 41902 41903 41904 41905 41906 41907 41908 41909 41910 41911 |
|| szPage&(szPage-1)
|| szPage>SQLITE_MAX_PAGE_SIZE
|| szPage<512
){
goto finished;
}
pWal->hdr.bigEndCksum = (u8)(magic&0x00000001);
pWal->szPage = szPage;
pWal->nCkpt = sqlite3Get4byte(&aBuf[12]);
memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);
/* Verify that the WAL header checksum is correct */
walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN,
aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum
);
|
| ︙ | ︙ | |||
41329 41330 41331 41332 41333 41334 41335 |
rc = walIndexAppend(pWal, ++iFrame, pgno);
if( rc!=SQLITE_OK ) break;
/* If nTruncate is non-zero, this is a commit record. */
if( nTruncate ){
pWal->hdr.mxFrame = iFrame;
pWal->hdr.nPage = nTruncate;
| | > > | 41947 41948 41949 41950 41951 41952 41953 41954 41955 41956 41957 41958 41959 41960 41961 41962 41963 |
rc = walIndexAppend(pWal, ++iFrame, pgno);
if( 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];
}
}
sqlite3_free(aFrame);
}
|
| ︙ | ︙ | |||
41354 41355 41356 41357 41358 41359 41360 41361 41362 41363 41364 41365 41366 41367 |
** currently holding locks that exclude all other readers, writers and
** checkpointers.
*/
pInfo = walCkptInfo(pWal);
pInfo->nBackfill = 0;
pInfo->aReadMark[0] = 0;
for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
}
recovery_error:
WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok"));
walUnlockExclusive(pWal, iLock, nLock);
return rc;
}
| > > > > > > > > > > > | 41974 41975 41976 41977 41978 41979 41980 41981 41982 41983 41984 41985 41986 41987 41988 41989 41990 41991 41992 41993 41994 41995 41996 41997 41998 |
** currently holding locks that exclude all other readers, writers and
** checkpointers.
*/
pInfo = walCkptInfo(pWal);
pInfo->nBackfill = 0;
pInfo->aReadMark[0] = 0;
for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
/* 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_OK, "Recovered %d frames from WAL file %s",
pWal->hdr.nPage, pWal->zWalName
);
}
}
recovery_error:
WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok"));
walUnlockExclusive(pWal, iLock, nLock);
return rc;
}
|
| ︙ | ︙ | |||
41714 41715 41716 41717 41718 41719 41720 |
static int walCheckpoint(
Wal *pWal, /* Wal connection */
int sync_flags, /* Flags for OsSync() (or 0) */
int nBuf, /* Size of zBuf in bytes */
u8 *zBuf /* Temporary buffer to use */
){
int rc; /* Return code */
| | > > > > | > > > > > > > > > > > > > | | 42345 42346 42347 42348 42349 42350 42351 42352 42353 42354 42355 42356 42357 42358 42359 42360 42361 42362 42363 42364 42365 42366 42367 42368 42369 42370 42371 42372 42373 42374 42375 42376 42377 42378 42379 42380 42381 42382 42383 42384 42385 42386 42387 42388 42389 42390 42391 42392 42393 42394 42395 42396 42397 42398 42399 42400 42401 42402 42403 42404 42405 42406 42407 42408 42409 42410 42411 42412 42413 42414 42415 42416 42417 42418 42419 42420 42421 42422 42423 42424 42425 42426 42427 42428 42429 42430 42431 42432 42433 42434 42435 42436 |
static int walCheckpoint(
Wal *pWal, /* Wal connection */
int sync_flags, /* Flags for OsSync() (or 0) */
int nBuf, /* Size of zBuf in bytes */
u8 *zBuf /* Temporary buffer to use */
){
int rc; /* Return code */
int szPage; /* Database page-size */
WalIterator *pIter = 0; /* Wal iterator context */
u32 iDbpage = 0; /* Next database page to write */
u32 iFrame = 0; /* Wal frame containing data for iDbpage */
u32 mxSafeFrame; /* Max frame that can be backfilled */
u32 mxPage; /* Max database page to write */
int i; /* Loop counter */
volatile WalCkptInfo *pInfo; /* The checkpoint status information */
szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
testcase( szPage<=32768 );
testcase( szPage>=65536 );
if( pWal->hdr.mxFrame==0 ) return SQLITE_OK;
/* Allocate the iterator */
rc = walIteratorInit(pWal, &pIter);
if( rc!=SQLITE_OK ){
return rc;
}
assert( pIter );
/*** TODO: Move this test out to the caller. Make it an assert() here ***/
if( szPage!=nBuf ){
rc = SQLITE_CORRUPT_BKPT;
goto walcheckpoint_out;
}
/* Compute in mxSafeFrame the index of the last frame of the WAL that is
** safe to write into the database. Frames beyond mxSafeFrame might
** overwrite database pages that are in use by active readers and thus
** cannot be backfilled from the WAL.
*/
mxSafeFrame = pWal->hdr.mxFrame;
mxPage = pWal->hdr.nPage;
pInfo = walCkptInfo(pWal);
for(i=1; i<WAL_NREADER; i++){
u32 y = pInfo->aReadMark[i];
if( mxSafeFrame>=y ){
assert( y<=pWal->hdr.mxFrame );
rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
if( rc==SQLITE_OK ){
pInfo->aReadMark[i] = READMARK_NOT_USED;
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
}else if( rc==SQLITE_BUSY ){
mxSafeFrame = y;
}else{
goto walcheckpoint_out;
}
}
}
if( pInfo->nBackfill<mxSafeFrame
&& (rc = walLockExclusive(pWal, WAL_READ_LOCK(0), 1))==SQLITE_OK
){
i64 nSize; /* Current size of database file */
u32 nBackfill = pInfo->nBackfill;
/* Sync the WAL to disk */
if( sync_flags ){
rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
}
/* If the database file may grow as a result of this checkpoint, hint
** about the eventual size of the db file to the VFS layer.
*/
if( rc==SQLITE_OK ){
i64 nReq = ((i64)mxPage * szPage);
rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
if( rc==SQLITE_OK && nSize<nReq ){
sqlite3OsFileControl(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( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ) continue;
iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE;
/* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */
rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset);
if( rc!=SQLITE_OK ) break;
iOffset = (iDbpage-1)*(i64)szPage;
testcase( IS_BIG_INT(iOffset) );
rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
|
| ︙ | ︙ | |||
41881 41882 41883 41884 41885 41886 41887 | u32 aCksum[2]; /* Checksum on the header content */ WalIndexHdr h1, h2; /* Two copies of the header content */ WalIndexHdr volatile *aHdr; /* Header in shared memory */ /* The first page of the wal-index must be mapped at this point. */ assert( pWal->nWiData>0 && pWal->apWiData[0] ); | | | 42529 42530 42531 42532 42533 42534 42535 42536 42537 42538 42539 42540 42541 42542 42543 | u32 aCksum[2]; /* Checksum on the header content */ WalIndexHdr h1, h2; /* Two copies of the header content */ WalIndexHdr volatile *aHdr; /* Header in shared memory */ /* The first page of the wal-index must be mapped at this point. */ assert( pWal->nWiData>0 && pWal->apWiData[0] ); /* Read the header. This might happen concurrently with a write to the ** same area of shared memory on a different CPU in a SMP, ** meaning it is possible that an inconsistent snapshot is read ** from the file. If this happens, return non-zero. ** ** There are two copies of the header at the beginning of the wal-index. ** When reading, read [0] first then [1]. Writes are in the reverse order. ** Memory barriers are used to prevent the compiler or the hardware from |
| ︙ | ︙ | |||
41910 41911 41912 41913 41914 41915 41916 |
if( aCksum[0]!=h1.aCksum[0] || aCksum[1]!=h1.aCksum[1] ){
return 1; /* Checksum does not match */
}
if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){
*pChanged = 1;
memcpy(&pWal->hdr, &h1, sizeof(WalIndexHdr));
| > | > | 42558 42559 42560 42561 42562 42563 42564 42565 42566 42567 42568 42569 42570 42571 42572 42573 42574 |
if( aCksum[0]!=h1.aCksum[0] || aCksum[1]!=h1.aCksum[1] ){
return 1; /* Checksum does not match */
}
if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){
*pChanged = 1;
memcpy(&pWal->hdr, &h1, sizeof(WalIndexHdr));
pWal->szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
testcase( pWal->szPage<=32768 );
testcase( pWal->szPage>=65536 );
}
/* The header was successfully read. Return zero. */
return 0;
}
/*
|
| ︙ | ︙ | |||
42229 42230 42231 42232 42233 42234 42235 42236 42237 42238 42239 42240 42241 42242 |
}
/*
** Finish with a read transaction. All this does is release the
** read-lock.
*/
SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){
if( pWal->readLock>=0 ){
walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
pWal->readLock = -1;
}
}
/*
| > | 42879 42880 42881 42882 42883 42884 42885 42886 42887 42888 42889 42890 42891 42892 42893 |
}
/*
** Finish with a read transaction. All this does is release the
** read-lock.
*/
SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){
sqlite3WalEndWriteTransaction(pWal);
if( pWal->readLock>=0 ){
walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
pWal->readLock = -1;
}
}
/*
|
| ︙ | ︙ | |||
42339 42340 42341 42342 42343 42344 42345 |
}
#endif
/* If iRead is non-zero, then it is the log frame number that contains the
** required page. Read and return data from the log file.
*/
if( iRead ){
| > > > > > > | | | | | > > | 42990 42991 42992 42993 42994 42995 42996 42997 42998 42999 43000 43001 43002 43003 43004 43005 43006 43007 43008 43009 43010 43011 43012 43013 43014 43015 43016 43017 43018 43019 43020 43021 43022 43023 43024 43025 43026 43027 43028 |
}
#endif
/* If iRead is non-zero, then it is the log frame number that contains the
** required page. Read and return data from the log file.
*/
if( iRead ){
int sz;
i64 iOffset;
sz = pWal->hdr.szPage;
sz = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
testcase( sz<=32768 );
testcase( sz>=65536 );
iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE;
*pInWal = 1;
/* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
return sqlite3OsRead(pWal->pWalFd, pOut, nOut, iOffset);
}
*pInWal = 0;
return SQLITE_OK;
}
/*
** Return the size of the database in pages (or zero, if unknown).
*/
SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal){
if( pWal && ALWAYS(pWal->readLock>=0) ){
return pWal->hdr.nPage;
}
return 0;
}
/*
** This function starts a write transaction on the WAL.
**
** A read transaction must have already been started by a prior call
|
| ︙ | ︙ | |||
42431 42432 42433 42434 42435 42436 42437 |
** returned to the caller.
**
** Otherwise, if the callback function does not return an error, this
** function returns SQLITE_OK.
*/
SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
int rc = SQLITE_OK;
| | | 43090 43091 43092 43093 43094 43095 43096 43097 43098 43099 43100 43101 43102 43103 43104 |
** returned to the caller.
**
** Otherwise, if the callback function does not return an error, this
** function returns SQLITE_OK.
*/
SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
int rc = SQLITE_OK;
if( ALWAYS(pWal->writeLock) ){
Pgno iMax = pWal->hdr.mxFrame;
Pgno iFrame;
/* Restore the clients cache of the wal-index header to the state it
** was in before the client began writing to the database.
*/
memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
|
| ︙ | ︙ | |||
42620 42621 42622 42623 42624 42625 42626 |
sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
sqlite3_randomness(8, pWal->hdr.aSalt);
memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
sqlite3Put4byte(&aWalHdr[28], aCksum[1]);
| | | 43279 43280 43281 43282 43283 43284 43285 43286 43287 43288 43289 43290 43291 43292 43293 |
sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
sqlite3_randomness(8, pWal->hdr.aSalt);
memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
sqlite3Put4byte(&aWalHdr[28], aCksum[1]);
pWal->szPage = szPage;
pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
pWal->hdr.aFrameCksum[0] = aCksum[0];
pWal->hdr.aFrameCksum[1] = aCksum[1];
rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0);
WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok"));
if( rc!=SQLITE_OK ){
|
| ︙ | ︙ | |||
42715 42716 42717 42718 42719 42720 42721 |
iFrame++;
nLast--;
rc = walIndexAppend(pWal, iFrame, pLast->pgno);
}
if( rc==SQLITE_OK ){
/* Update the private copy of the header. */
| | > > | 43374 43375 43376 43377 43378 43379 43380 43381 43382 43383 43384 43385 43386 43387 43388 43389 43390 |
iFrame++;
nLast--;
rc = walIndexAppend(pWal, iFrame, pLast->pgno);
}
if( rc==SQLITE_OK ){
/* Update the private copy of the header. */
pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
testcase( szPage<=32768 );
testcase( szPage>=65536 );
pWal->hdr.mxFrame = iFrame;
if( isCommit ){
pWal->hdr.iChange++;
pWal->hdr.nPage = nTruncate;
}
/* If this is a commit, update the wal-index header too. */
if( isCommit ){
|
| ︙ | ︙ | |||
42927 42928 42929 42930 42931 42932 42933 | ** page has a small header which contains the Ptr(N) pointer and other ** information such as the size of key and data. ** ** FORMAT DETAILS ** ** The file is divided into pages. The first page is called page 1, ** the second is page 2, and so forth. A page number of zero indicates | | | 43588 43589 43590 43591 43592 43593 43594 43595 43596 43597 43598 43599 43600 43601 43602 | ** page has a small header which contains the Ptr(N) pointer and other ** information such as the size of key and data. ** ** FORMAT DETAILS ** ** The file is divided into pages. The first page is called page 1, ** the second is page 2, and so forth. A page number of zero indicates ** "no such page". The page size can be any power of 2 between 512 and 65536. ** Each page can be either a btree page, a freelist page, an overflow ** page, or a pointer-map page. ** ** The first page is always a btree page. The first 100 bytes of the first ** page contain a special header (the "file header") that describes the file. ** The format of the file header is as follows: ** |
| ︙ | ︙ | |||
43293 43294 43295 43296 43297 43298 43299 | u8 pageSizeFixed; /* True if the page size can no longer be changed */ u8 secureDelete; /* True if secure_delete is enabled */ u8 initiallyEmpty; /* Database is empty at start of transaction */ #ifndef SQLITE_OMIT_AUTOVACUUM u8 autoVacuum; /* True if auto-vacuum is enabled */ u8 incrVacuum; /* True if incr-vacuum is enabled */ #endif | < < > > | 43954 43955 43956 43957 43958 43959 43960 43961 43962 43963 43964 43965 43966 43967 43968 43969 43970 43971 43972 43973 43974 43975 | u8 pageSizeFixed; /* True if the page size can no longer be changed */ u8 secureDelete; /* True if secure_delete is enabled */ u8 initiallyEmpty; /* Database is empty at start of transaction */ #ifndef SQLITE_OMIT_AUTOVACUUM u8 autoVacuum; /* True if auto-vacuum is enabled */ u8 incrVacuum; /* True if incr-vacuum is enabled */ #endif u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ u8 inTransaction; /* Transaction state */ u8 doNotUseWAL; /* If true, do not open write-ahead-log file */ u32 pageSize; /* Total number of bytes on a page */ u32 usableSize; /* Number of usable bytes on each page */ int nTransaction; /* Number of open transactions (read + write) */ u32 nPage; /* Number of pages in the database */ void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */ Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ #ifndef SQLITE_OMIT_SHARED_CACHE |
| ︙ | ︙ | |||
43899 43900 43901 43902 43903 43904 43905 |
#if 0
int sqlite3BtreeTrace=1; /* True to enable tracing */
# define TRACE(X) if(sqlite3BtreeTrace){printf X;fflush(stdout);}
#else
# define TRACE(X)
#endif
| | > > > > > > > > > | 44560 44561 44562 44563 44564 44565 44566 44567 44568 44569 44570 44571 44572 44573 44574 44575 44576 44577 44578 44579 44580 44581 44582 44583 |
#if 0
int sqlite3BtreeTrace=1; /* True to enable tracing */
# define TRACE(X) if(sqlite3BtreeTrace){printf X;fflush(stdout);}
#else
# define TRACE(X)
#endif
/*
** Extract a 2-byte big-endian integer from an array of unsigned bytes.
** But if the value is zero, make it 65536.
**
** This routine is used to extract the "offset to cell content area" value
** from the header of a btree page. If the page size is 65536 and the page
** is empty, the offset should be 65536, but the 2-byte value stores zero.
** This routine makes the necessary adjustment to 65536.
*/
#define get2byteNotZero(X) (((((int)get2byte(X))-1)&0xffff)+1)
#ifndef SQLITE_OMIT_SHARED_CACHE
/*
** A list of BtShared objects that are eligible for participation
** in shared cache. This variable has file scope during normal builds,
** but the test harness needs to access it so we make it global for
** test builds.
|
| ︙ | ︙ | |||
44588 44589 44590 44591 44592 44593 44594 44595 44596 44597 44598 44599 44600 44601 44602 44603 44604 44605 44606 |
}
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Given a page number of a regular database page, return the page
** number for the pointer-map page that contains the entry for the
** input page number.
*/
static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){
int nPagesPerMapPage;
Pgno iPtrMap, ret;
assert( sqlite3_mutex_held(pBt->mutex) );
nPagesPerMapPage = (pBt->usableSize/5)+1;
iPtrMap = (pgno-2)/nPagesPerMapPage;
ret = (iPtrMap*nPagesPerMapPage) + 2;
if( ret==PENDING_BYTE_PAGE(pBt) ){
ret++;
}
return ret;
| > > > > > | 45258 45259 45260 45261 45262 45263 45264 45265 45266 45267 45268 45269 45270 45271 45272 45273 45274 45275 45276 45277 45278 45279 45280 45281 |
}
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Given a page number of a regular database page, return the page
** number for the pointer-map page that contains the entry for the
** input page number.
**
** Return 0 (not a valid page) for pgno==1 since there is
** no pointer map associated with page 1. The integrity_check logic
** requires that ptrmapPageno(*,1)!=1.
*/
static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){
int nPagesPerMapPage;
Pgno iPtrMap, ret;
assert( sqlite3_mutex_held(pBt->mutex) );
if( pgno<2 ) return 0;
nPagesPerMapPage = (pBt->usableSize/5)+1;
iPtrMap = (pgno-2)/nPagesPerMapPage;
ret = (iPtrMap*nPagesPerMapPage) + 2;
if( ret==PENDING_BYTE_PAGE(pBt) ){
ret++;
}
return ret;
|
| ︙ | ︙ | |||
45021 45022 45023 45024 45025 45026 45027 | assert( pPage->nOverflow==0 ); usableSize = pPage->pBt->usableSize; assert( nByte < usableSize-8 ); nFrag = data[hdr+7]; assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf ); gap = pPage->cellOffset + 2*pPage->nCell; | | | | 45696 45697 45698 45699 45700 45701 45702 45703 45704 45705 45706 45707 45708 45709 45710 45711 45712 45713 45714 45715 45716 45717 45718 45719 45720 |
assert( pPage->nOverflow==0 );
usableSize = pPage->pBt->usableSize;
assert( nByte < usableSize-8 );
nFrag = data[hdr+7];
assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf );
gap = pPage->cellOffset + 2*pPage->nCell;
top = get2byteNotZero(&data[hdr+5]);
if( gap>top ) return SQLITE_CORRUPT_BKPT;
testcase( gap+2==top );
testcase( gap+1==top );
testcase( gap==top );
if( nFrag>=60 ){
/* Always defragment highly fragmented pages */
rc = defragmentPage(pPage);
if( rc ) return rc;
top = get2byteNotZero(&data[hdr+5]);
}else if( gap+2<=top ){
/* Search the freelist looking for a free slot big enough to satisfy
** the request. The allocation is made from the first free slot in
** the list that is large enough to accomadate it.
*/
int pc, addr;
for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){
|
| ︙ | ︙ | |||
45073 45074 45075 45076 45077 45078 45079 |
/* Check to make sure there is enough space in the gap to satisfy
** the allocation. If not, defragment.
*/
testcase( gap+2+nByte==top );
if( gap+2+nByte>top ){
rc = defragmentPage(pPage);
if( rc ) return rc;
| | | 45748 45749 45750 45751 45752 45753 45754 45755 45756 45757 45758 45759 45760 45761 45762 |
/* Check to make sure there is enough space in the gap to satisfy
** the allocation. If not, defragment.
*/
testcase( gap+2+nByte==top );
if( gap+2+nByte>top ){
rc = defragmentPage(pPage);
if( rc ) return rc;
top = get2byteNotZero(&data[hdr+5]);
assert( gap+nByte<=top );
}
/* Allocate memory from the gap in between the cell pointer array
** and the cell content area. The btreeInitPage() call has already
** validated the freelist. Given that the freelist is valid, there
|
| ︙ | ︙ | |||
45239 45240 45241 45242 45243 45244 45245 |
assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) );
if( !pPage->isInit ){
u16 pc; /* Address of a freeblock within pPage->aData[] */
u8 hdr; /* Offset to beginning of page header */
u8 *data; /* Equal to pPage->aData */
BtShared *pBt; /* The main btree structure */
| | | | | | | | 45914 45915 45916 45917 45918 45919 45920 45921 45922 45923 45924 45925 45926 45927 45928 45929 45930 45931 45932 45933 45934 45935 45936 45937 45938 45939 45940 45941 45942 45943 45944 45945 |
assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) );
if( !pPage->isInit ){
u16 pc; /* Address of a freeblock within pPage->aData[] */
u8 hdr; /* Offset to beginning of page header */
u8 *data; /* Equal to pPage->aData */
BtShared *pBt; /* The main btree structure */
int usableSize; /* Amount of usable space on each page */
u16 cellOffset; /* Offset from start of page to first cell pointer */
int nFree; /* Number of unused bytes on the page */
int top; /* First byte of the cell content area */
int iCellFirst; /* First allowable cell or freeblock offset */
int iCellLast; /* Last possible cell or freeblock offset */
pBt = pPage->pBt;
hdr = pPage->hdrOffset;
data = pPage->aData;
if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT;
assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
pPage->maskPage = (u16)(pBt->pageSize - 1);
pPage->nOverflow = 0;
usableSize = pBt->usableSize;
pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;
top = get2byteNotZero(&data[hdr+5]);
pPage->nCell = get2byte(&data[hdr+3]);
if( pPage->nCell>MX_CELL(pBt) ){
/* To many cells for a single page. The page must be corrupt */
return SQLITE_CORRUPT_BKPT;
}
testcase( pPage->nCell==MX_CELL(pBt) );
|
| ︙ | ︙ | |||
45355 45356 45357 45358 45359 45360 45361 |
memset(&data[hdr], 0, pBt->usableSize - hdr);
}
data[hdr] = (char)flags;
first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0);
memset(&data[hdr+1], 0, 4);
data[hdr+7] = 0;
put2byte(&data[hdr+5], pBt->usableSize);
| | | | | 46030 46031 46032 46033 46034 46035 46036 46037 46038 46039 46040 46041 46042 46043 46044 46045 46046 46047 46048 46049 46050 |
memset(&data[hdr], 0, pBt->usableSize - hdr);
}
data[hdr] = (char)flags;
first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0);
memset(&data[hdr+1], 0, 4);
data[hdr+7] = 0;
put2byte(&data[hdr+5], pBt->usableSize);
pPage->nFree = (u16)(pBt->usableSize - first);
decodeFlags(pPage, flags);
pPage->hdrOffset = hdr;
pPage->cellOffset = first;
pPage->nOverflow = 0;
assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
pPage->maskPage = (u16)(pBt->pageSize - 1);
pPage->nCell = 0;
pPage->isInit = 1;
}
/*
** Convert a DbPage obtained from the pager into a MemPage used by
|
| ︙ | ︙ | |||
45669 45670 45671 45672 45673 45674 45675 |
pBt->pCursor = 0;
pBt->pPage1 = 0;
pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager);
#ifdef SQLITE_SECURE_DELETE
pBt->secureDelete = 1;
#endif
| | | 46344 46345 46346 46347 46348 46349 46350 46351 46352 46353 46354 46355 46356 46357 46358 |
pBt->pCursor = 0;
pBt->pPage1 = 0;
pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager);
#ifdef SQLITE_SECURE_DELETE
pBt->secureDelete = 1;
#endif
pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16);
if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
|| ((pBt->pageSize-1)&pBt->pageSize)!=0 ){
pBt->pageSize = 0;
#ifndef SQLITE_OMIT_AUTOVACUUM
/* If the magic name ":memory:" will create an in-memory database, then
** leave the autoVacuum mode at 0 (do not auto-vacuum), even if
** SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if
|
| ︙ | ︙ | |||
45983 45984 45985 45986 45987 45988 45989 |
nReserve = pBt->pageSize - pBt->usableSize;
}
assert( nReserve>=0 && nReserve<=255 );
if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE &&
((pageSize-1)&pageSize)==0 ){
assert( (pageSize & 7)==0 );
assert( !pBt->pPage1 && !pBt->pCursor );
| | | 46658 46659 46660 46661 46662 46663 46664 46665 46666 46667 46668 46669 46670 46671 46672 |
nReserve = pBt->pageSize - pBt->usableSize;
}
assert( nReserve>=0 && nReserve<=255 );
if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE &&
((pageSize-1)&pageSize)==0 ){
assert( (pageSize & 7)==0 );
assert( !pBt->pPage1 && !pBt->pCursor );
pBt->pageSize = (u32)pageSize;
freeTempSpace(pBt);
}
rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve);
pBt->usableSize = pBt->pageSize - (u16)nReserve;
if( iFix ) pBt->pageSizeFixed = 1;
sqlite3BtreeLeave(p);
return rc;
|
| ︙ | ︙ | |||
46118 46119 46120 46121 46122 46123 46124 | rc = btreeGetPage(pBt, 1, &pPage1, 0); if( rc!=SQLITE_OK ) return rc; /* Do some checking to help insure the file we opened really is ** a valid database file. */ nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData); | | < < | | | 46793 46794 46795 46796 46797 46798 46799 46800 46801 46802 46803 46804 46805 46806 46807 46808 46809 46810 46811 46812 46813 |
rc = btreeGetPage(pBt, 1, &pPage1, 0);
if( rc!=SQLITE_OK ) return rc;
/* Do some checking to help insure the file we opened really is
** a valid database file.
*/
nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData);
sqlite3PagerPagecount(pBt->pPager, &nPageFile);
if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){
nPage = nPageFile;
}
if( nPage>0 ){
u32 pageSize;
u32 usableSize;
u8 *page1 = pPage1->aData;
rc = SQLITE_NOTADB;
if( memcmp(page1, zMagicHeader, 16)!=0 ){
goto page1_init_failed;
}
#ifdef SQLITE_OMIT_WAL
|
| ︙ | ︙ | |||
46177 46178 46179 46180 46181 46182 46183 |
** embedded fraction must be 12.5% for both leaf-data and non-leaf-data.
** The original design allowed these amounts to vary, but as of
** version 3.6.0, we require them to be fixed.
*/
if( memcmp(&page1[21], "\100\040\040",3)!=0 ){
goto page1_init_failed;
}
| | | | > | | | | | | | | | | | 46850 46851 46852 46853 46854 46855 46856 46857 46858 46859 46860 46861 46862 46863 46864 46865 46866 46867 46868 46869 46870 46871 46872 46873 46874 46875 46876 46877 46878 46879 46880 46881 46882 46883 46884 46885 46886 46887 46888 46889 46890 46891 46892 46893 46894 46895 46896 46897 46898 46899 46900 46901 46902 46903 46904 46905 46906 46907 46908 46909 46910 46911 46912 46913 46914 46915 46916 46917 46918 46919 |
** embedded fraction must be 12.5% for both leaf-data and non-leaf-data.
** The original design allowed these amounts to vary, but as of
** version 3.6.0, we require them to be fixed.
*/
if( memcmp(&page1[21], "\100\040\040",3)!=0 ){
goto page1_init_failed;
}
pageSize = (page1[16]<<8) | (page1[17]<<16);
if( ((pageSize-1)&pageSize)!=0
|| pageSize>SQLITE_MAX_PAGE_SIZE
|| pageSize<=256
){
goto page1_init_failed;
}
assert( (pageSize & 7)==0 );
usableSize = pageSize - page1[20];
if( (u32)pageSize!=pBt->pageSize ){
/* After reading the first page of the database assuming a page size
** of BtShared.pageSize, we have discovered that the page-size is
** actually pageSize. Unlock the database, leave pBt->pPage1 at
** zero and return SQLITE_OK. The caller will call this function
** again with the correct page-size.
*/
releasePage(pPage1);
pBt->usableSize = usableSize;
pBt->pageSize = pageSize;
freeTempSpace(pBt);
rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize,
pageSize-usableSize);
return rc;
}
if( nPageHeader>nPageFile ){
rc = SQLITE_CORRUPT_BKPT;
goto page1_init_failed;
}
if( usableSize<480 ){
goto page1_init_failed;
}
pBt->pageSize = pageSize;
pBt->usableSize = usableSize;
#ifndef SQLITE_OMIT_AUTOVACUUM
pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0);
pBt->incrVacuum = (get4byte(&page1[36 + 7*4])?1:0);
#endif
}
/* maxLocal is the maximum amount of payload to store locally for
** a cell. Make sure it is small enough so that at least minFanout
** cells can will fit on one page. We assume a 10-byte page header.
** Besides the payload, the cell must store:
** 2-byte pointer to the cell
** 4-byte child pointer
** 9-byte nKey value
** 4-byte nData value
** 4-byte overflow page pointer
** So a cell consists of a 2-byte pointer, a header which is as much as
** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow
** page pointer.
*/
pBt->maxLocal = (u16)((pBt->usableSize-12)*64/255 - 23);
pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23);
pBt->maxLeaf = (u16)(pBt->usableSize - 35);
pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23);
assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) );
pBt->pPage1 = pPage1;
pBt->nPage = nPage;
return SQLITE_OK;
page1_init_failed:
releasePage(pPage1);
|
| ︙ | ︙ | |||
46284 46285 46286 46287 46288 46289 46290 | pP1 = pBt->pPage1; assert( pP1!=0 ); data = pP1->aData; rc = sqlite3PagerWrite(pP1->pDbPage); if( rc ) return rc; memcpy(data, zMagicHeader, sizeof(zMagicHeader)); assert( sizeof(zMagicHeader)==16 ); | | > | 46958 46959 46960 46961 46962 46963 46964 46965 46966 46967 46968 46969 46970 46971 46972 46973 | pP1 = pBt->pPage1; assert( pP1!=0 ); data = pP1->aData; rc = sqlite3PagerWrite(pP1->pDbPage); if( rc ) return rc; memcpy(data, zMagicHeader, sizeof(zMagicHeader)); assert( sizeof(zMagicHeader)==16 ); data[16] = (u8)((pBt->pageSize>>8)&0xff); data[17] = (u8)((pBt->pageSize>>16)&0xff); data[18] = 1; data[19] = 1; assert( pBt->usableSize<=pBt->pageSize && pBt->usableSize+255>=pBt->pageSize); data[20] = (u8)(pBt->pageSize - pBt->usableSize); data[21] = 64; data[22] = 32; data[23] = 32; |
| ︙ | ︙ | |||
48295 48296 48297 48298 48299 48300 48301 |
}else{
assert( nCellKey>intKey );
c = +1;
}
pCur->validNKey = 1;
pCur->info.nKey = nCellKey;
}else{
| | | | 48970 48971 48972 48973 48974 48975 48976 48977 48978 48979 48980 48981 48982 48983 48984 48985 48986 |
}else{
assert( nCellKey>intKey );
c = +1;
}
pCur->validNKey = 1;
pCur->info.nKey = nCellKey;
}else{
/* The maximum supported page-size is 65536 bytes. This means that
** the maximum number of record bytes stored on an index B-Tree
** page is less than 16384 bytes and may be stored as a 2-byte
** varint. This information is used to attempt to avoid parsing
** the entire cell by checking for the cases where the record is
** stored entirely within the b-tree page by inspecting the first
** 2 bytes of the cell.
*/
int nCell = pCell[0];
if( !(nCell & 0x80) && nCell<=pPage->maxLocal ){
|
| ︙ | ︙ | |||
48660 48661 48662 48663 48664 48665 48666 48667 48668 48669 48670 48671 48672 48673 |
if( rc ){
goto end_allocate_page;
}
if( k==0 ){
if( !pPrevTrunk ){
memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
}else{
memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4);
}
}else{
/* The trunk page is required by the caller but it contains
** pointers to free-list leaves. The first leaf becomes a trunk
** page in this case.
*/
| > > > > | 49335 49336 49337 49338 49339 49340 49341 49342 49343 49344 49345 49346 49347 49348 49349 49350 49351 49352 |
if( rc ){
goto end_allocate_page;
}
if( k==0 ){
if( !pPrevTrunk ){
memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
}else{
rc = sqlite3PagerWrite(pPrevTrunk->pDbPage);
if( rc!=SQLITE_OK ){
goto end_allocate_page;
}
memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4);
}
}else{
/* The trunk page is required by the caller but it contains
** pointers to free-list leaves. The first leaf becomes a trunk
** page in this case.
*/
|
| ︙ | ︙ | |||
48966 48967 48968 48969 48970 48971 48972 |
*/
static int clearCell(MemPage *pPage, unsigned char *pCell){
BtShared *pBt = pPage->pBt;
CellInfo info;
Pgno ovflPgno;
int rc;
int nOvfl;
| | | 49645 49646 49647 49648 49649 49650 49651 49652 49653 49654 49655 49656 49657 49658 49659 |
*/
static int clearCell(MemPage *pPage, unsigned char *pCell){
BtShared *pBt = pPage->pBt;
CellInfo info;
Pgno ovflPgno;
int rc;
int nOvfl;
u32 ovflPageSize;
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
btreeParseCellPtr(pPage, pCell, &info);
if( info.iOverflow==0 ){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
}
ovflPgno = get4byte(&pCell[info.iOverflow]);
|
| ︙ | ︙ | |||
49191 49192 49193 49194 49195 49196 49197 |
** the cell content has been copied someplace else. This routine just
** removes the reference to the cell from pPage.
**
** "sz" must be the number of bytes in the cell.
*/
static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
int i; /* Loop counter */
| | | | 49870 49871 49872 49873 49874 49875 49876 49877 49878 49879 49880 49881 49882 49883 49884 49885 49886 49887 49888 49889 49890 49891 49892 49893 49894 49895 49896 49897 49898 49899 49900 49901 49902 |
** the cell content has been copied someplace else. This routine just
** removes the reference to the cell from pPage.
**
** "sz" must be the number of bytes in the cell.
*/
static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
int i; /* Loop counter */
u32 pc; /* Offset to cell content of cell being deleted */
u8 *data; /* pPage->aData */
u8 *ptr; /* Used to move bytes around within data[] */
int rc; /* The return code */
int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */
if( *pRC ) return;
assert( idx>=0 && idx<pPage->nCell );
assert( sz==cellSize(pPage, idx) );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
data = pPage->aData;
ptr = &data[pPage->cellOffset + 2*idx];
pc = get2byte(ptr);
hdr = pPage->hdrOffset;
testcase( pc==get2byte(&data[hdr+5]) );
testcase( pc+sz==pPage->pBt->usableSize );
if( pc < (u32)get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){
*pRC = SQLITE_CORRUPT_BKPT;
return;
}
rc = freeSpace(pPage, pc, sz);
if( rc ){
*pRC = rc;
return;
|
| ︙ | ︙ | |||
49266 49267 49268 49269 49270 49271 49272 | u8 *ptr; /* Used for moving information around in data[] */ int nSkip = (iChild ? 4 : 0); if( *pRC ) return; assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); | | | 49945 49946 49947 49948 49949 49950 49951 49952 49953 49954 49955 49956 49957 49958 49959 | u8 *ptr; /* Used for moving information around in data[] */ int nSkip = (iChild ? 4 : 0); if( *pRC ) return; assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 ); assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); /* The cell should normally be sized correctly. However, when moving a ** malformed cell from a leaf page to an interior page, if the cell size ** wanted to be less than 4 but got rounded up to 4 on the leaf, then size ** might be less than 8 (leaf-size + pointer) on the interior node. Hence ** the term after the || in the following assert(). */ |
| ︙ | ︙ | |||
49346 49347 49348 49349 49350 49351 49352 | int cellbody; /* Address of next cell body */ u8 * const data = pPage->aData; /* Pointer to data for pPage */ const int hdr = pPage->hdrOffset; /* Offset of header on pPage */ const int nUsable = pPage->pBt->usableSize; /* Usable size of page */ assert( pPage->nOverflow==0 ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); | | | | 50025 50026 50027 50028 50029 50030 50031 50032 50033 50034 50035 50036 50037 50038 50039 50040 50041 50042 50043 50044 |
int cellbody; /* Address of next cell body */
u8 * const data = pPage->aData; /* Pointer to data for pPage */
const int hdr = pPage->hdrOffset; /* Offset of header on pPage */
const int nUsable = pPage->pBt->usableSize; /* Usable size of page */
assert( pPage->nOverflow==0 );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( nCell>=0 && nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921);
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
/* Check that the page has just been zeroed by zeroPage() */
assert( pPage->nCell==0 );
assert( get2byteNotZero(&data[hdr+5])==nUsable );
pCellptr = &data[pPage->cellOffset + nCell*2];
cellbody = nUsable;
for(i=nCell-1; i>=0; i--){
pCellptr -= 2;
cellbody -= aSize[i];
put2byte(pCellptr, cellbody);
|
| ︙ | ︙ | |||
49417 49418 49419 49420 49421 49422 49423 49424 49425 49426 49427 49428 49429 49430 | int rc; /* Return Code */ Pgno pgnoNew; /* Page number of pNew */ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( sqlite3PagerIswriteable(pParent->pDbPage) ); assert( pPage->nOverflow==1 ); if( pPage->nCell<=0 ) return SQLITE_CORRUPT_BKPT; /* Allocate a new page. This page will become the right-sibling of ** pPage. Make the parent page writable, so that the new divider cell ** may be inserted. If both these operations are successful, proceed. */ rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); | > | 50096 50097 50098 50099 50100 50101 50102 50103 50104 50105 50106 50107 50108 50109 50110 | int rc; /* Return Code */ Pgno pgnoNew; /* Page number of pNew */ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( sqlite3PagerIswriteable(pParent->pDbPage) ); assert( pPage->nOverflow==1 ); /* This error condition is now caught prior to reaching this function */ if( pPage->nCell<=0 ) return SQLITE_CORRUPT_BKPT; /* Allocate a new page. This page will become the right-sibling of ** pPage. Make the parent page writable, so that the new divider cell ** may be inserted. If both these operations are successful, proceed. */ rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); |
| ︙ | ︙ | |||
49746 49747 49748 49749 49750 49751 49752 |
** Unless SQLite is compiled in secure-delete mode. In this case,
** the dropCell() routine will overwrite the entire cell with zeroes.
** In this case, temporarily copy the cell into the aOvflSpace[]
** buffer. It will be copied out again as soon as the aSpace[] buffer
** is allocated. */
if( pBt->secureDelete ){
int iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
| | | 50426 50427 50428 50429 50430 50431 50432 50433 50434 50435 50436 50437 50438 50439 50440 |
** Unless SQLite is compiled in secure-delete mode. In this case,
** the dropCell() routine will overwrite the entire cell with zeroes.
** In this case, temporarily copy the cell into the aOvflSpace[]
** buffer. It will be copied out again as soon as the aSpace[] buffer
** is allocated. */
if( pBt->secureDelete ){
int iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
if( (iOff+szNew[i])>(int)pBt->usableSize ){
rc = SQLITE_CORRUPT_BKPT;
memset(apOld, 0, (i+1)*sizeof(MemPage*));
goto balance_cleanup;
}else{
memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]);
apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData];
}
|
| ︙ | ︙ | |||
49825 49826 49827 49828 49829 49830 49831 |
if( i<nOld-1 && !leafData){
u16 sz = (u16)szNew[i];
u8 *pTemp;
assert( nCell<nMaxCells );
szCell[nCell] = sz;
pTemp = &aSpace1[iSpace1];
iSpace1 += sz;
| | | 50505 50506 50507 50508 50509 50510 50511 50512 50513 50514 50515 50516 50517 50518 50519 |
if( i<nOld-1 && !leafData){
u16 sz = (u16)szNew[i];
u8 *pTemp;
assert( nCell<nMaxCells );
szCell[nCell] = sz;
pTemp = &aSpace1[iSpace1];
iSpace1 += sz;
assert( sz<=pBt->maxLocal+23 );
assert( iSpace1<=pBt->pageSize );
memcpy(pTemp, apDiv[i], sz);
apCell[nCell] = pTemp+leafCorrection;
assert( leafCorrection==0 || leafCorrection==4 );
szCell[nCell] = szCell[nCell] - leafCorrection;
if( !pOld->leaf ){
assert( leafCorrection==0 );
|
| ︙ | ︙ | |||
50071 50072 50073 50074 50075 50076 50077 |
*/
if( szCell[j]==4 ){
assert(leafCorrection==4);
sz = cellSizePtr(pParent, pCell);
}
}
iOvflSpace += sz;
| | | 50751 50752 50753 50754 50755 50756 50757 50758 50759 50760 50761 50762 50763 50764 50765 |
*/
if( szCell[j]==4 ){
assert(leafCorrection==4);
sz = cellSizePtr(pParent, pCell);
}
}
iOvflSpace += sz;
assert( sz<=pBt->maxLocal+23 );
assert( iOvflSpace<=pBt->pageSize );
insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc);
if( rc!=SQLITE_OK ) goto balance_cleanup;
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
j++;
nxDiv++;
|
| ︙ | ︙ | |||
51315 51316 51317 51318 51319 51320 51321 |
if( isFreeList ){
int n = get4byte(&pOvflData[4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext);
}
#endif
| | | 51995 51996 51997 51998 51999 52000 52001 52002 52003 52004 52005 52006 52007 52008 52009 |
if( isFreeList ){
int n = get4byte(&pOvflData[4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext);
}
#endif
if( n>(int)pCheck->pBt->usableSize/4-2 ){
checkAppendMsg(pCheck, zContext,
"freelist leaf count too big on page %d", iPage);
N--;
}else{
for(i=0; i<n; i++){
Pgno iFreePage = get4byte(&pOvflData[8+i*4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
|
| ︙ | ︙ | |||
51526 51527 51528 51529 51530 51531 51532 |
*/
data = pPage->aData;
hdr = pPage->hdrOffset;
hit = sqlite3PageMalloc( pBt->pageSize );
if( hit==0 ){
pCheck->mallocFailed = 1;
}else{
| | | | | 52206 52207 52208 52209 52210 52211 52212 52213 52214 52215 52216 52217 52218 52219 52220 52221 52222 52223 52224 52225 52226 52227 52228 52229 52230 52231 52232 52233 |
*/
data = pPage->aData;
hdr = pPage->hdrOffset;
hit = sqlite3PageMalloc( pBt->pageSize );
if( hit==0 ){
pCheck->mallocFailed = 1;
}else{
int contentOffset = get2byteNotZero(&data[hdr+5]);
assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
memset(hit+contentOffset, 0, usableSize-contentOffset);
memset(hit, 1, contentOffset);
nCell = get2byte(&data[hdr+3]);
cellStart = hdr + 12 - 4*pPage->leaf;
for(i=0; i<nCell; i++){
int pc = get2byte(&data[cellStart+i*2]);
u32 size = 65536;
int j;
if( pc<=usableSize-4 ){
size = cellSizePtr(pPage, &data[pc]);
}
if( (int)(pc+size-1)>=usableSize ){
checkAppendMsg(pCheck, 0,
"Corruption detected in cell %d on page %d",i,iPage);
}else{
for(j=pc+size-1; j>=pc; j--) hit[j]++;
}
}
i = get2byte(&data[hdr+1]);
|
| ︙ | ︙ | |||
52162 52163 52164 52165 52166 52167 52168 52169 52170 52171 52172 52173 52174 52175 |
/* Catch the case where the destination is an in-memory database and the
** page sizes of the source and destination differ.
*/
if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){
rc = SQLITE_READONLY;
}
/* This loop runs once for each destination page spanned by the source
** page. For each iteration, variable iOff is set to the byte offset
** of the destination page.
*/
for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOff<iEnd; iOff+=nDestPgsz){
DbPage *pDestPg = 0;
| > > > > > > > > > | 52842 52843 52844 52845 52846 52847 52848 52849 52850 52851 52852 52853 52854 52855 52856 52857 52858 52859 52860 52861 52862 52863 52864 |
/* Catch the case where the destination is an in-memory database and the
** page sizes of the source and destination differ.
*/
if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){
rc = SQLITE_READONLY;
}
#ifdef SQLITE_HAS_CODEC
/* Backup is not possible if the page size of the destination is changing
** a a codec is in use.
*/
if( nSrcPgsz!=nDestPgsz && sqlite3PagerGetCodec(pDestPager)!=0 ){
rc = SQLITE_READONLY;
}
#endif
/* This loop runs once for each destination page spanned by the source
** page. For each iteration, variable iOff is set to the byte offset
** of the destination page.
*/
for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOff<iEnd; iOff+=nDestPgsz){
DbPage *pDestPg = 0;
|
| ︙ | ︙ | |||
55767 55768 55769 55770 55771 55772 55773 |
/* Check for one of the special errors */
mrc = p->rc & 0xff;
assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */
isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
|| mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
if( isSpecialError ){
| | > > > > > > > > > | | 56456 56457 56458 56459 56460 56461 56462 56463 56464 56465 56466 56467 56468 56469 56470 56471 56472 56473 56474 56475 56476 56477 56478 56479 56480 |
/* Check for one of the special errors */
mrc = p->rc & 0xff;
assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */
isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
|| mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
if( isSpecialError ){
/* If the query was read-only and the error code is SQLITE_INTERRUPT,
** no rollback is necessary. Otherwise, at least a savepoint
** transaction must be rolled back to restore the database to a
** consistent state.
**
** Even if the statement is read-only, it is important to perform
** a statement or transaction rollback operation. If the error
** occured while writing to the journal, sub-journal or database
** file as part of an effort to free up cache space (see function
** pagerStress() in pager.c), the rollback is required to restore
** the pager to a consistent state.
*/
if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){
eStatementOp = SAVEPOINT_ROLLBACK;
}else{
/* We are forced to roll back the active transaction. Before doing
** so, abort any other statements this handle currently has active.
|
| ︙ | ︙ | |||
63198 63199 63200 63201 63202 63203 63204 | ** then jump to P2. Otherwise fall through to the next instruction. ** ** If P5 is non-zero then the key value is increased by an epsilon ** prior to the comparison. This make the opcode work like IdxGT except ** that if the key from register P3 is a prefix of the key in the cursor, ** the result is false whereas it would be true with IdxGT. */ | | | 63896 63897 63898 63899 63900 63901 63902 63903 63904 63905 63906 63907 63908 63909 63910 | ** then jump to P2. Otherwise fall through to the next instruction. ** ** If P5 is non-zero then the key value is increased by an epsilon ** prior to the comparison. This make the opcode work like IdxGT except ** that if the key from register P3 is a prefix of the key in the cursor, ** the result is false whereas it would be true with IdxGT. */ /* Opcode: IdxLT P1 P2 P3 P4 P5 ** ** The P4 register values beginning with P3 form an unpacked index ** key that omits the ROWID. Compare this key value against the index ** that P1 is currently pointing to, ignoring the ROWID on the P1 index. ** ** If the P1 index entry is less than the key value then jump to P2. ** Otherwise fall through to the next instruction. |
| ︙ | ︙ | |||
67545 67546 67547 67548 67549 67550 67551 |
if( z[1]==0 ){
/* Wildcard of the form "?". Assign the next variable number */
assert( z[0]=='?' );
pExpr->iColumn = (ynVar)(++pParse->nVar);
}else if( z[0]=='?' ){
/* Wildcard of the form "?nnn". Convert "nnn" to an integer and
** use it as the variable number */
| > | | | | 68243 68244 68245 68246 68247 68248 68249 68250 68251 68252 68253 68254 68255 68256 68257 68258 68259 68260 68261 68262 68263 68264 68265 68266 68267 68268 68269 |
if( z[1]==0 ){
/* Wildcard of the form "?". Assign the next variable number */
assert( z[0]=='?' );
pExpr->iColumn = (ynVar)(++pParse->nVar);
}else if( z[0]=='?' ){
/* Wildcard of the form "?nnn". Convert "nnn" to an integer and
** use it as the variable number */
i64 i;
int bOk = sqlite3Atoi64(&z[1], &i);
pExpr->iColumn = (ynVar)i;
testcase( i==0 );
testcase( i==1 );
testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 );
testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] );
if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
}
if( i>pParse->nVar ){
pParse->nVar = (int)i;
}
}else{
/* Wildcards like ":aaa", "$aaa" or "@aaa". Reuse the same variable
** number as the prior appearance of the same name, or if the name
** has never appeared before, reuse the same variable number
*/
int i;
|
| ︙ | ︙ | |||
72090 72091 72092 72093 72094 72095 72096 72097 72098 72099 72100 72101 72102 72103 |
if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){
sqlite3DbFree(db, p->u.z);
}
}
sqlite3DbFree(db, pIdx->aSample);
}
#else
UNUSED_PARAMETER(pIdx);
#endif
}
/*
** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
| > | 72789 72790 72791 72792 72793 72794 72795 72796 72797 72798 72799 72800 72801 72802 72803 |
if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){
sqlite3DbFree(db, p->u.z);
}
}
sqlite3DbFree(db, pIdx->aSample);
}
#else
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(pIdx);
#endif
}
/*
** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
|
| ︙ | ︙ | |||
87736 87737 87738 87739 87740 87741 87742 | ** The data to be output is contained in pIn->iMem. There are ** pIn->nMem columns to be output. pDest is where the output should ** be sent. ** ** regReturn is the number of the register holding the subroutine ** return address. ** | | | 88436 88437 88438 88439 88440 88441 88442 88443 88444 88445 88446 88447 88448 88449 88450 | ** The data to be output is contained in pIn->iMem. There are ** pIn->nMem columns to be output. pDest is where the output should ** be sent. ** ** regReturn is the number of the register holding the subroutine ** return address. ** ** If regPrev>0 then it is the first register in a vector that ** records the previous output. mem[regPrev] is a flag that is false ** if there has been no previous output. If regPrev>0 then code is ** generated to suppress duplicates. pKeyInfo is used for comparing ** keys. ** ** If the LIMIT found in p->iLimit is reached, jump immediately to ** iBreak. |
| ︙ | ︙ | |||
88433 88434 88435 88436 88437 88438 88439 | ** Flattening is only attempted if all of the following are true: ** ** (1) The subquery and the outer query do not both use aggregates. ** ** (2) The subquery is not an aggregate or the outer query is not a join. ** ** (3) The subquery is not the right operand of a left outer join | | | | > | | | | 89133 89134 89135 89136 89137 89138 89139 89140 89141 89142 89143 89144 89145 89146 89147 89148 89149 89150 89151 89152 89153 89154 89155 89156 89157 89158 89159 89160 89161 89162 89163 89164 89165 89166 89167 89168 89169 89170 89171 89172 89173 89174 89175 | ** Flattening is only attempted if all of the following are true: ** ** (1) The subquery and the outer query do not both use aggregates. ** ** (2) The subquery is not an aggregate or the outer query is not a join. ** ** (3) The subquery is not the right operand of a left outer join ** (Originally ticket #306. Strengthened by ticket #3300) ** ** (4) The subquery is not DISTINCT. ** ** (**) At one point restrictions (4) and (5) defined a subset of DISTINCT ** sub-queries that were excluded from this optimization. Restriction ** (4) has since been expanded to exclude all DISTINCT subqueries. ** ** (6) The subquery does not use aggregates or the outer query is not ** DISTINCT. ** ** (7) The subquery has a FROM clause. ** ** (8) The subquery does not use LIMIT or the outer query is not a join. ** ** (9) The subquery does not use LIMIT or the outer query does not use ** aggregates. ** ** (10) The subquery does not use aggregates or the outer query does not ** use LIMIT. ** ** (11) The subquery and the outer query do not both have ORDER BY clauses. ** ** (**) Not implemented. Subsumed into restriction (3). Was previously ** a separate restriction deriving from ticket #350. ** ** (13) The subquery and outer query do not both use LIMIT. ** ** (14) The subquery does not use OFFSET. ** ** (15) The outer query is not part of a compound select or the ** subquery does not have a LIMIT clause. ** (See ticket #2339 and ticket [02a8e81d44]). ** ** (16) The outer query is not an aggregate or the subquery does ** not contain ORDER BY. (Ticket #2942) This used to not matter |
| ︙ | ︙ | |||
88551 88552 88553 88554 88555 88556 88557 |
** and (14). */
if( pSub->pLimit && p->pLimit ) return 0; /* Restriction (13) */
if( pSub->pOffset ) return 0; /* Restriction (14) */
if( p->pRightmost && pSub->pLimit ){
return 0; /* Restriction (15) */
}
if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */
| | | | | 89252 89253 89254 89255 89256 89257 89258 89259 89260 89261 89262 89263 89264 89265 89266 89267 89268 |
** and (14). */
if( pSub->pLimit && p->pLimit ) return 0; /* Restriction (13) */
if( pSub->pOffset ) return 0; /* Restriction (14) */
if( p->pRightmost && pSub->pLimit ){
return 0; /* Restriction (15) */
}
if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */
if( pSub->selFlags & SF_Distinct ) return 0; /* Restriction (5) */
if( pSub->pLimit && (pSrc->nSrc>1 || isAgg) ){
return 0; /* Restrictions (8)(9) */
}
if( (p->selFlags & SF_Distinct)!=0 && subqueryIsAgg ){
return 0; /* Restriction (6) */
}
if( p->pOrderBy && pSub->pOrderBy ){
return 0; /* Restriction (11) */
}
|
| ︙ | ︙ | |||
93223 93224 93225 93226 93227 93228 93229 |
pTab->nCol = pParse->pNewTable->nCol;
pParse->pNewTable->nCol = 0;
pParse->pNewTable->aCol = 0;
}
db->pVTab = 0;
}else{
sqlite3Error(db, SQLITE_ERROR, zErr);
| | | 93924 93925 93926 93927 93928 93929 93930 93931 93932 93933 93934 93935 93936 93937 93938 |
pTab->nCol = pParse->pNewTable->nCol;
pParse->pNewTable->nCol = 0;
pParse->pNewTable->aCol = 0;
}
db->pVTab = 0;
}else{
sqlite3Error(db, SQLITE_ERROR, zErr);
sqlite3DbFree(db, zErr);
rc = SQLITE_ERROR;
}
pParse->declareVtab = 0;
if( pParse->pVdbe ){
sqlite3VdbeFinalize(pParse->pVdbe);
}
|
| ︙ | ︙ | |||
96857 96858 96859 96860 96861 96862 96863 |
** If there are no inequality constraints, then N is at
** least one.
**
** This case is also used when there are no WHERE clause
** constraints but an index is selected anyway, in order
** to force the output order to conform to an ORDER BY.
*/
| | | | | | | | | 97558 97559 97560 97561 97562 97563 97564 97565 97566 97567 97568 97569 97570 97571 97572 97573 97574 97575 97576 97577 97578 97579 97580 97581 97582 97583 97584 97585 97586 97587 97588 97589 97590 97591 97592 97593 97594 97595 97596 97597 97598 97599 97600 |
** If there are no inequality constraints, then N is at
** least one.
**
** This case is also used when there are no WHERE clause
** constraints but an index is selected anyway, in order
** to force the output order to conform to an ORDER BY.
*/
static const u8 aStartOp[] = {
0,
0,
OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */
OP_Last, /* 3: (!start_constraints && startEq && bRev) */
OP_SeekGt, /* 4: (start_constraints && !startEq && !bRev) */
OP_SeekLt, /* 5: (start_constraints && !startEq && bRev) */
OP_SeekGe, /* 6: (start_constraints && startEq && !bRev) */
OP_SeekLe /* 7: (start_constraints && startEq && bRev) */
};
static const u8 aEndOp[] = {
OP_Noop, /* 0: (!end_constraints) */
OP_IdxGE, /* 1: (end_constraints && !bRev) */
OP_IdxLT /* 2: (end_constraints && bRev) */
};
int nEq = pLevel->plan.nEq; /* Number of == or IN terms */
int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */
int regBase; /* Base register holding constraint values */
int r1; /* Temp register */
WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
int startEq; /* True if range start uses ==, >= or <= */
int endEq; /* True if range end uses ==, >= or <= */
int start_constraints; /* Start of range is constrained */
int nConstraint; /* Number of constraint terms */
Index *pIdx; /* The index we will be using */
int iIdxCur; /* The VDBE cursor for the index */
int nExtraReg = 0; /* Number of extra registers needed */
int op; /* Instruction opcode */
char *zStartAff; /* Affinity for start of range constraint */
char *zEndAff; /* Affinity for end of range constraint */
pIdx = pLevel->plan.u.pIdx;
iIdxCur = pLevel->iIdxCur;
k = pIdx->aiColumn[nEq]; /* Column for inequality constraints */
|
| ︙ | ︙ | |||
108718 108719 108720 108721 108722 108723 108724 | /* ** The fts3 built-in tokenizers - "simple" and "porter" - are implemented ** in files fts3_tokenizer1.c and fts3_porter.c respectively. The following ** two forward declarations are for functions declared in these files ** used to retrieve the respective implementations. ** ** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed | | | 109419 109420 109421 109422 109423 109424 109425 109426 109427 109428 109429 109430 109431 109432 109433 | /* ** The fts3 built-in tokenizers - "simple" and "porter" - are implemented ** in files fts3_tokenizer1.c and fts3_porter.c respectively. The following ** two forward declarations are for functions declared in these files ** used to retrieve the respective implementations. ** ** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed ** to by the argument to point to the "simple" tokenizer implementation. ** Function ...PorterTokenizerModule() sets *pModule to point to the ** porter tokenizer/stemmer implementation. */ SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule); SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule); SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule); |
| ︙ | ︙ | |||
108920 108921 108922 108923 108924 108925 108926 |
** an integer that falls outside of the range of the unsigned char type
** is undefined (and sometimes, "undefined" means segfault). This wrapper
** is defined to accept an argument of type char, and always returns 0 for
** any values that fall outside of the range of the unsigned char type (i.e.
** negative values).
*/
static int fts3isspace(char c){
| | | 109621 109622 109623 109624 109625 109626 109627 109628 109629 109630 109631 109632 109633 109634 109635 |
** an integer that falls outside of the range of the unsigned char type
** is undefined (and sometimes, "undefined" means segfault). This wrapper
** is defined to accept an argument of type char, and always returns 0 for
** any values that fall outside of the range of the unsigned char type (i.e.
** negative values).
*/
static int fts3isspace(char c){
return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
}
/*
** Extract the next token from buffer z (length n) using the tokenizer
** and other information (column names etc.) in pParse. Create an Fts3Expr
** structure of type FTSQUERY_PHRASE containing a phrase consisting of this
** single token and set *ppExpr to point to it. If the end of the buffer is
|
| ︙ | ︙ | |||
111307 111308 111309 111310 111311 111312 111313 111314 111315 111316 111317 111318 111319 111320 |
int nTokenAllocated; /* space allocated to zToken buffer */
} simple_tokenizer_cursor;
static int simpleDelim(simple_tokenizer *t, unsigned char c){
return c<0x80 && t->delim[c];
}
/*
** Create a new tokenizer instance.
*/
static int simpleCreate(
int argc, const char * const *argv,
sqlite3_tokenizer **ppTokenizer
| > > > | 112008 112009 112010 112011 112012 112013 112014 112015 112016 112017 112018 112019 112020 112021 112022 112023 112024 |
int nTokenAllocated; /* space allocated to zToken buffer */
} simple_tokenizer_cursor;
static int simpleDelim(simple_tokenizer *t, unsigned char c){
return c<0x80 && t->delim[c];
}
static int fts3_isalnum(int x){
return (x>='0' && x<='9') || (x>='A' && x<='Z') || (x>='a' && x<='z');
}
/*
** Create a new tokenizer instance.
*/
static int simpleCreate(
int argc, const char * const *argv,
sqlite3_tokenizer **ppTokenizer
|
| ︙ | ︙ | |||
111341 111342 111343 111344 111345 111346 111347 |
}
t->delim[ch] = 1;
}
} else {
/* Mark non-alphanumeric ASCII characters as delimiters */
int i;
for(i=1; i<0x80; i++){
| | | 112045 112046 112047 112048 112049 112050 112051 112052 112053 112054 112055 112056 112057 112058 112059 |
}
t->delim[ch] = 1;
}
} else {
/* Mark non-alphanumeric ASCII characters as delimiters */
int i;
for(i=1; i<0x80; i++){
t->delim[i] = !fts3_isalnum(i) ? -1 : 0;
}
}
*ppTokenizer = &t->base;
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
111447 111448 111449 111450 111451 111452 111453 |
c->pToken = pNew;
}
for(i=0; i<n; i++){
/* TODO(shess) This needs expansion to handle UTF-8
** case-insensitivity.
*/
unsigned char ch = p[iStartOffset+i];
| | | 112151 112152 112153 112154 112155 112156 112157 112158 112159 112160 112161 112162 112163 112164 112165 |
c->pToken = pNew;
}
for(i=0; i<n; i++){
/* TODO(shess) This needs expansion to handle UTF-8
** case-insensitivity.
*/
unsigned char ch = p[iStartOffset+i];
c->pToken[i] = (char)((ch>='A' && ch<='Z') ? ch-'A'+'a' : ch);
}
*ppToken = c->pToken;
*pnBytes = n;
*piStartOffset = iStartOffset;
*piEndOffset = c->iOffset;
*piPosition = c->iToken++;
|
| ︙ | ︙ | |||
116353 116354 116355 116356 116357 116358 116359 | ** Rtree virtual table module xBestIndex method. There are three ** table scan strategies to choose from (in order from most to ** least desirable): ** ** idxNum idxStr Strategy ** ------------------------------------------------ ** 1 Unused Direct lookup by rowid. | | < | | 117057 117058 117059 117060 117061 117062 117063 117064 117065 117066 117067 117068 117069 117070 117071 117072 117073 117074 | ** Rtree virtual table module xBestIndex method. There are three ** table scan strategies to choose from (in order from most to ** least desirable): ** ** idxNum idxStr Strategy ** ------------------------------------------------ ** 1 Unused Direct lookup by rowid. ** 2 See below R-tree query or full-table scan. ** ------------------------------------------------ ** ** If strategy 1 is used, then idxStr is not meaningful. If strategy ** 2 is used, idxStr is formatted to contain 2 bytes for each ** constraint used. The first two bytes of idxStr correspond to ** the constraint in sqlite3_index_info.aConstraintUsage[] with ** (argvIndex==1) etc. ** ** The first of each pair of bytes in idxStr identifies the constraint ** operator as follows: |
| ︙ | ︙ |
Changes to src/sqlite3.h.
| ︙ | ︙ | |||
93 94 95 96 97 98 99 | ** The SQLITE_VERSION_NUMBER for any given release of SQLite will also ** be larger than the release from which it is derived. Either Y will ** be held constant and Z will be incremented or else Y will be incremented ** and Z will be reset to zero. ** ** Since version 3.6.18, SQLite source code has been stored in the ** <a href="http://www.fossil-scm.org/">Fossil configuration management | | | | | | 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 | ** The SQLITE_VERSION_NUMBER for any given release of SQLite will also ** be larger than the release from which it is derived. Either Y will ** be held constant and Z will be incremented or else Y will be incremented ** and Z will be reset to zero. ** ** Since version 3.6.18, SQLite source code has been stored in the ** <a href="http://www.fossil-scm.org/">Fossil configuration management ** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to ** a string which identifies a particular check-in of SQLite ** within its configuration management system. ^The SQLITE_SOURCE_ID ** string contains the date and time of the check-in (UTC) and an SHA1 ** hash of the entire source tree. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.2" #define SQLITE_VERSION_NUMBER 3007002 #define SQLITE_SOURCE_ID "2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3" /* ** 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 |
| ︙ | ︙ | |||
150 151 152 153 154 155 156 | ** CAPI3REF: Run-Time Library Compilation Options Diagnostics ** ** ^The sqlite3_compileoption_used() function returns 0 or 1 ** indicating whether the specified option was defined at ** compile time. ^The SQLITE_ prefix may be omitted from the ** option name passed to sqlite3_compileoption_used(). ** | | | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | ** CAPI3REF: Run-Time Library Compilation Options Diagnostics ** ** ^The sqlite3_compileoption_used() function returns 0 or 1 ** indicating whether the specified option was defined at ** compile time. ^The SQLITE_ prefix may be omitted from the ** option name passed to sqlite3_compileoption_used(). ** ** ^The sqlite3_compileoption_get() function allows iterating ** over the list of options that were defined at compile time by ** returning the N-th compile time option string. ^If N is out of range, ** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_ ** prefix is omitted from any strings returned by ** sqlite3_compileoption_get(). ** ** ^Support for the diagnostic functions sqlite3_compileoption_used() ** and sqlite3_compileoption_get() may be omitted by specifying the ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. ** ** See also: SQL functions [sqlite_compileoption_used()] and ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS SQLITE_API int sqlite3_compileoption_used(const char *zOptName); |
| ︙ | ︙ | |||
264 265 266 267 268 269 270 | #endif /* ** CAPI3REF: Closing A Database Connection ** ** ^The sqlite3_close() routine is the destructor for the [sqlite3] object. ** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is | | | 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | #endif /* ** CAPI3REF: Closing A Database Connection ** ** ^The sqlite3_close() routine is the destructor for the [sqlite3] object. ** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is ** successfully destroyed and all associated resources are deallocated. ** ** Applications must [sqlite3_finalize | finalize] all [prepared statements] ** and [sqlite3_blob_close | close] all [BLOB handles] associated with ** the [sqlite3] object prior to attempting to close the object. ^If ** sqlite3_close() is called on a [database connection] that still has ** outstanding [prepared statements] or [BLOB handles], then it returns ** SQLITE_BUSY. |
| ︙ | ︙ | |||
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 | ** ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the ** current transaction. This hint is not guaranteed to be accurate but it ** is often close. The underlying VFS might choose to preallocate database ** file space based on this hint in order to help writes to the database ** file run faster. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only | > > > > > > > > > | 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 | ** ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the ** current transaction. This hint is not guaranteed to be accurate but it ** is often close. The underlying VFS might choose to preallocate database ** file space based on this hint in order to help writes to the database ** file run faster. ** ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS ** extends and truncates the database file in chunks of a size specified ** by the user. The fourth argument to [sqlite3_file_control()] should ** point to an integer (type int) containing the new chunk-size to use ** for the nominated database. Allocating database file space in large ** chunks (say 1MB at a time), may reduce file-system fragmentation and ** improve performance on some systems. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only |
| ︙ | ︙ | |||
2659 2660 2661 2662 2663 2664 2665 | ** <li> ?NNN ** <li> :VVV ** <li> @VVV ** <li> $VVV ** </ul> ** ** In the templates above, NNN represents an integer literal, | | | 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 | ** <li> ?NNN ** <li> :VVV ** <li> @VVV ** <li> $VVV ** </ul> ** ** In the templates above, NNN represents an integer literal, ** and VVV represents an alphanumeric identifier.)^ ^The values of these ** parameters (also called "host parameter names" or "SQL parameters") ** can be set using the sqlite3_bind_*() routines defined here. ** ** ^The first argument to the sqlite3_bind_*() routines is always ** a pointer to the [sqlite3_stmt] object returned from ** [sqlite3_prepare_v2()] or its variants. ** |
| ︙ | ︙ | |||
3438 3439 3440 3441 3442 3443 3444 | SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context ** | | | 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 | SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context ** ** Implementations of aggregate SQL functions use this ** routine to allocate memory for storing their state. ** ** ^The first time the sqlite3_aggregate_context(C,N) routine is called ** for a particular aggregate function, SQLite ** allocates N of memory, zeroes out that memory, and returns a pointer ** to the new memory. ^On second and subsequent calls to ** sqlite3_aggregate_context() for the same aggregate function instance, |
| ︙ | ︙ | |||
3710 3711 3712 3713 3714 3715 3716 | ** expects pointers to be UTF-16 strings in the native byte order, or the ** argument can be [SQLITE_UTF16_ALIGNED] if the ** the routine expects pointers to 16-bit word aligned strings ** of UTF-16 in the native byte order. ** ** A pointer to the user supplied routine must be passed as the fifth ** argument. ^If it is NULL, this is the same as deleting the collation | | | 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 | ** expects pointers to be UTF-16 strings in the native byte order, or the ** argument can be [SQLITE_UTF16_ALIGNED] if the ** the routine expects pointers to 16-bit word aligned strings ** of UTF-16 in the native byte order. ** ** A pointer to the user supplied routine must be passed as the fifth ** argument. ^If it is NULL, this is the same as deleting the collation ** sequence (so that SQLite cannot call it any more). ** ^Each time the application supplied function is invoked, it is passed ** as its first parameter a copy of the void* passed as the fourth argument ** to sqlite3_create_collation() or sqlite3_create_collation16(). ** ** ^The remaining arguments to the application-supplied routine are two strings, ** each represented by a (length, data) pair and encoded in the encoding ** that was passed as the third argument when the collation sequence was |
| ︙ | ︙ | |||
4928 4929 4930 4931 4932 4933 4934 | ** of a valid mutex handle. The implementations of the methods defined ** by this structure are not required to handle this case, the results ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). ** ** The xMutexInit() method must be threadsafe. ^It must be harmless to | | | 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 | ** of a valid mutex handle. The implementations of the methods defined ** by this structure are not required to handle this case, the results ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). ** ** The xMutexInit() method must be threadsafe. ^It must be harmless to ** invoke xMutexInit() multiple times within the same process and without ** intervening calls to xMutexEnd(). Second and subsequent calls to ** xMutexInit() must be no-ops. ** ** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] ** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory ** allocation for a static mutex. ^However xMutexAlloc() may use SQLite ** memory allocation for a fast or recursive mutex. |
| ︙ | ︙ | |||
5098 5099 5100 5101 5102 5103 5104 | #define SQLITE_TESTCTRL_PGHDRSZ 17 #define SQLITE_TESTCTRL_LAST 17 /* ** CAPI3REF: SQLite Runtime Status ** ** ^This interface is used to retrieve runtime status information | | | 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 | #define SQLITE_TESTCTRL_PGHDRSZ 17 #define SQLITE_TESTCTRL_LAST 17 /* ** CAPI3REF: SQLite Runtime Status ** ** ^This interface is used to retrieve runtime status information ** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes ** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^ ** ^The current value of the parameter is returned into *pCurrent. ** ^The highest recorded value is returned in *pHighwater. ^If the ** resetFlag is true, then the highest record value is reset after ** *pHighwater is written. ^(Some parameters do not record the highest |
| ︙ | ︙ | |||
5224 5225 5226 5227 5228 5229 5230 | ** CAPI3REF: Database Connection Status ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the ** database connection object to be interrogated. ^The second argument ** is an integer constant, taken from the set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that | | | 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 | ** CAPI3REF: Database Connection Status ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the ** database connection object to be interrogated. ^The second argument ** is an integer constant, taken from the set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that ** determines the parameter to interrogate. The set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely ** to grow in future releases of SQLite. ** ** ^The current value of the requested parameter is written into *pCur ** and the highest instantaneous value is written into *pHiwtr. ^If ** the resetFlg is true, then the highest instantaneous value is ** reset back down to the current value. |
| ︙ | ︙ | |||
5646 5647 5648 5649 5650 5651 5652 | ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** ** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b> ** ** ^Each call to sqlite3_backup_step() sets two values inside ** the [sqlite3_backup] object: the number of pages still to be backed | | | 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 | ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** ** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b> ** ** ^Each call to sqlite3_backup_step() sets two values inside ** the [sqlite3_backup] object: the number of pages still to be backed ** up and the total number of pages in the source database file. ** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces ** retrieve these two values, respectively. ** ** ^The values returned by these functions are only updated by ** sqlite3_backup_step(). ^If the source database is modified during a backup ** operation, then the values are not updated to account for any extra ** pages that need to be updated or the size of the source database file |
| ︙ | ︙ | |||
5742 5743 5744 5745 5746 5747 5748 | ** the other connections to use as the blocking connection. ** ** ^(There may be at most one unlock-notify callback registered by a ** blocked connection. If sqlite3_unlock_notify() is called when the ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing | | | 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 | ** the other connections to use as the blocking connection. ** ** ^(There may be at most one unlock-notify callback registered by a ** blocked connection. If sqlite3_unlock_notify() is called when the ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing ** unlock-notify callback is canceled. ^The blocked connections ** unlock-notify callback may also be canceled by closing the blocked ** connection using [sqlite3_close()]. ** ** The unlock-notify callback is not reentrant. If an application invokes ** any sqlite3_xxx API functions from within an unlock-notify callback, a ** crash or deadlock may be the result. ** |
| ︙ | ︙ | |||
5824 5825 5826 5827 5828 5829 5830 | /* ** CAPI3REF: String Comparison ** ** ^The [sqlite3_strnicmp()] API allows applications and extensions to ** compare the contents of two buffers containing UTF-8 strings in a | | | 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 | /* ** CAPI3REF: String Comparison ** ** ^The [sqlite3_strnicmp()] API allows applications and extensions to ** compare the contents of two buffers containing UTF-8 strings in a ** case-independent fashion, using the same definition of case independence ** that SQLite uses internally when comparing identifiers. */ SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); /* ** CAPI3REF: Error Logging Interface ** |
| ︙ | ︙ |
Changes to src/stat.c.
| ︙ | ︙ | |||
26 27 28 29 30 31 32 33 34 35 36 |
** WEBPAGE: stat
**
** Show statistics and global information about the repository.
*/
void stat_page(void){
i64 t;
int n, m, fsize;
char zBuf[100];
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
style_header("Repository Statistics");
| > > | > > | > > > > > | | 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 |
** WEBPAGE: stat
**
** Show statistics and global information about the repository.
*/
void stat_page(void){
i64 t;
int n, m, fsize;
int szMax, szAvg;
char zBuf[100];
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
style_header("Repository Statistics");
@ <table class="label-value">
@ <tr><th>Repository Size:</th><td>
fsize = file_size(g.zRepositoryName);
@ %d(fsize) bytes
@ </td></tr>
@ <tr><th>Number Of Artifacts:</th><td>
n = db_int(0, "SELECT count(*) FROM blob");
m = db_int(0, "SELECT count(*) FROM delta");
@ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs)
@ </td></tr>
if( n>0 ){
int a, b;
Stmt q;
@ <tr><th>Uncompressed Artifact Size:</th><td>
db_prepare(&q, "SELECT total(size), avg(size), max(size)"
" FROM blob WHERE size>0");
db_step(&q);
t = db_column_int64(&q, 0);
szAvg = db_column_int(&q, 1);
szMax = db_column_int(&q, 2);
db_finalize(&q);
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", t);
@ %d(szAvg) bytes average, %d(szMax) bytes max, %s(zBuf) bytes total
@ </td></tr>
@ <tr><th>Compression Ratio:</th><td>
if( t/fsize < 5 ){
b = 10;
fsize /= 10;
}else{
b = 1;
|
| ︙ | ︙ | |||
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
" WHERE +tagname GLOB 'tkt-*'");
@ %d(n)
@ </td></tr>
@ <tr><th>Duration Of Project:</th><td>
n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
" + 0.99");
@ %d(n) days
@ </td></tr>
@ <tr><th>Project ID:</th><td>
@ %h(db_get("project-code",""))
@ </td></tr>
@ <tr><th>Server ID:</th><td>
@ %h(db_get("server-code",""))
@ </td></tr>
@ <tr><th>Fossil Version:</th><td>
@ %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
@ </td></tr>
@ <tr><th>SQLite Version:</th><td>
| > > | | > | | 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 |
" WHERE +tagname GLOB 'tkt-*'");
@ %d(n)
@ </td></tr>
@ <tr><th>Duration Of Project:</th><td>
n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
" + 0.99");
@ %d(n) days
sqlite3_snprintf(sizeof(zBuf), zBuf, "%.2f", n/365.24);
@ or approximately %s(zBuf) years
@ </td></tr>
@ <tr><th>Project ID:</th><td>
@ %h(db_get("project-code",""))
@ </td></tr>
@ <tr><th>Server ID:</th><td>
@ %h(db_get("server-code",""))
@ </td></tr>
@ <tr><th>Fossil Version:</th><td>
@ %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
@ </td></tr>
@ <tr><th>SQLite Version:</th><td>
sqlite3_snprintf(sizeof(zBuf), zBuf, "%.19s [%.10s] (%s)",
SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], SQLITE_VERSION);
@ %s(zBuf)
@ </td></tr>
@ <tr><th>Database Stats:</th><td>
@ %d(db_int(0, "PRAGMA %s.page_count", g.zRepoDb)) pages,
@ %d(db_int(0, "PRAGMA %s.page_size", g.zRepoDb)) bytes/page,
@ %d(db_int(0, "PRAGMA %s.freelist_count", g.zRepoDb)) free pages,
@ %s(db_text(0, "PRAGMA %s.encoding", g.zRepoDb)),
@ %s(db_text(0, "PRAGMA %s.journal_mode", g.zRepoDb)) mode
@ </td></tr>
@ </table>
style_footer();
}
|
Changes to src/style.c.
| ︙ | ︙ | |||
81 82 83 84 85 86 87 |
va_start(ap, zTitleFormat);
zTitle = vmprintf(zTitleFormat, ap);
va_end(ap);
cgi_destination(CGI_HEADER);
cgi_printf("%s",
| | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
va_start(ap, zTitleFormat);
zTitle = vmprintf(zTitleFormat, ap);
va_end(ap);
cgi_destination(CGI_HEADER);
cgi_printf("%s",
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
" \"http://www.x3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1);
/* Generate the header up through the main menu */
Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
Th_Store("title", zTitle);
|
| ︙ | ︙ | |||
137 138 139 140 141 142 143 144 |
}
@ </div>
}
@ <div class="content">
cgi_destination(CGI_BODY);
/* Put the footer at the bottom of the page.
*/
| > > > | | | < < | | | | | | | | 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 |
}
@ </div>
}
@ <div class="content">
cgi_destination(CGI_BODY);
/* Put the footer at the bottom of the page.
** the additional clear/both is needed to extend the content
** part to the end of an optional sidebox.
*/
@ <div style="clear: both;"></div>
@ </div>
zFooter = db_get("footer", (char*)zDefaultFooter);
if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
Th_Render(zFooter);
if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
/* Render trace log if TH1 tracing is enabled. */
if( g.thTrace ){
cgi_append_content("<font color=\"red\"><hr />\n", -1);
cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
cgi_append_content("</font>\n", -1);
}
}
/*
** Begin a side-box on the right-hand side of a page. The title and
** the width of the box are given as arguments. The width is usually
** a percentage of total screen width.
*/
void style_sidebox_begin(const char *zTitle, const char *zWidth){
@ <div class="sidebox" style="width:%s(zWidth)">
@ <div class="sideboxTitle">%h(zTitle)</div>
}
/* End the side-box
*/
void style_sidebox_end(void){
@ </div>
}
/* @-comment: // */
/*
** The default page header.
*/
const char zDefaultHeader[] =
@ <html>
@ <head>
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@ href="$baseurl/timeline.rss" />
@ <link rel="stylesheet" href="$baseurl/style.css?default" type="text/css"
@ media="screen" />
@ </head>
@ <body>
@ <div class="header">
@ <div class="logo">
@ <img src="$baseurl/logo" alt="logo">
@ </div>
@ <div class="title"><small>$<project_name></small><br />$<title></div>
@ <div class="status"><th1>
@ if {[info exists login]} {
@ puts "Logged in as $login"
@ } else {
@ puts "Not logged in"
@ }
@ </th1></div>
@ </div>
@ <div class="mainmenu"><th1>
@ html "<a href='$baseurl$index_page'>Home</a> "
@ if {[anycap jor]} {
@ html "<a href='$baseurl/timeline'>Timeline</a> "
@ }
@ if {[hascap oh]} {
|
| ︙ | ︙ | |||
241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
@ Fossil version $manifest_version $manifest_date
@ </div>
@ </body></html>
;
/*
** The default Cascading Style Sheet.
*/
const char zDefaultCSS[] =
@ /* General settings for the entire page */
@ body {
@ margin: 0ex 1ex;
@ padding: 0px;
@ background-color: white;
| > > > > | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
@ Fossil version $manifest_version $manifest_date
@ </div>
@ </body></html>
;
/*
** The default Cascading Style Sheet.
** It's assembled by different strings for each class.
** The default css conatains all definitions.
** The style sheet, send to the client only contains the ones,
** not defined in the user defined css.
*/
const char zDefaultCSS[] =
@ /* General settings for the entire page */
@ body {
@ margin: 0ex 1ex;
@ padding: 0px;
@ background-color: white;
|
| ︙ | ︙ | |||
270 271 272 273 274 275 276 | @ display: table-cell; @ font-size: 2em; @ font-weight: bold; @ text-align: center; @ padding: 0 0 0 1em; @ color: #558195; @ vertical-align: bottom; | | > | | 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 |
@ display: table-cell;
@ font-size: 2em;
@ font-weight: bold;
@ text-align: center;
@ padding: 0 0 0 1em;
@ color: #558195;
@ vertical-align: bottom;
@ width: 100% ;
@ }
@
@ /* The login status message in the top right-hand corner */
@ div.status {
@ display: table-cell;
@ text-align: right;
@ vertical-align: bottom;
@ color: #558195;
@ font-size: 0.8em;
@ font-weight: bold;
@ min-width: 200px;
@ white-space: nowrap;
@ }
@
@ /* The header across the top of the page */
@ div.header {
@ display: table;
@ width: 100% ;
@ }
@
@ /* The main menu bar that appears at the top of the page beneath
@ ** the header */
@ div.mainmenu {
@ padding: 5px 10px 5px 10px;
@ font-size: 0.9em;
|
| ︙ | ︙ | |||
335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
@ margin-bottom: 0px;
@ margin-top: 1em;
@ padding: 1px 1px 1px 1px;
@ font-size: 1.2em;
@ font-weight: bold;
@ background-color: #558195;
@ color: white;
@ }
@
@ /* The "Date" that occurs on the left hand side of timelines */
@ div.divider {
@ background: #a1c4d4;
@ border: 2px #558195 solid;
@ font-size: 1em; font-weight: normal;
| > | 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
@ margin-bottom: 0px;
@ margin-top: 1em;
@ padding: 1px 1px 1px 1px;
@ font-size: 1.2em;
@ font-weight: bold;
@ background-color: #558195;
@ color: white;
@ white-space: nowrap;
@ }
@
@ /* The "Date" that occurs on the left hand side of timelines */
@ div.divider {
@ background: #a1c4d4;
@ border: 2px #558195 solid;
@ font-size: 1em; font-weight: normal;
|
| ︙ | ︙ | |||
364 365 366 367 368 369 370 |
@
@ /* Hyperlink colors in the footer */
@ div.footer a { color: white; }
@ div.footer a:link { color: white; }
@ div.footer a:visited { color: white; }
@ div.footer a:hover { background-color: white; color: #558195; }
@
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > | | | | < | 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 |
@
@ /* Hyperlink colors in the footer */
@ div.footer a { color: white; }
@ div.footer a:link { color: white; }
@ div.footer a:visited { color: white; }
@ div.footer a:hover { background-color: white; color: #558195; }
@
@ /* verbatim blocks */
@ pre.verbatim {
@ background-color: #f5f5f5;
@ padding: 0.5em;
@}
@
@ /* The label/value pairs on (for example) the ci page */
@ table.label-value th {
@ vertical-align: top;
@ text-align: right;
@ padding: 0.2ex 2ex;
@ }
@
;
/* The following table contains bits of default CSS that must
** be included if they are not found in the application-defined
** CSS.
*/
const struct strctCssDefaults {
char const * const elementClass; /* Name of element needed */
char const * const comment; /* Comment text */
char const * const value; /* CSS text */
} cssDefaultList[] = {
{ "",
"",
zDefaultCSS
},
{ "div.sidebox",
"The nomenclature sidebox for branches,..",
@ float: right;
@ background-color: white;
@ border-width: medium;
@ border-style: double;
@ margin: 10;
},
{ "div.sideboxTitle",
"The nomenclature title in sideboxes for branches,..",
@ display: inline;
@ font-weight: bold;
},
{ "div.sideboxDescribed",
"The defined element in sideboxes for branches,..",
@ display: inline;
@ font-weight: bold;
},
{ "span.disabled",
"The defined element in sideboxes for branches,..",
@ color: red;
},
{ "span.timelineDisabled",
"The suppressed duplicates lines in timeline, ..",
@ font-style: italic;
@ font-size: small;
},
{ "table.timelineTable",
"the format for the timeline data table",
@ cellspacing: 0;
@ border: 0;
@ cellpadding: 0
},
{ "td.timelineTableCell",
"the format for the timeline data cells",
@ valign: top;
@ align: left;
},
{ "span.timelineLeaf",
"the format for the timeline leaf marks",
@ font-weight: bold;
},
{ "a.timelineHistLink",
"the format for the timeline version links",
@
},
{ "span.timelineHistDsp",
"the format for the timeline version display(no history permission!)",
@ font-weight: bold;
},
{ "td.timelineTime",
"the format for the timeline time display",
@ vertical-align: top;
@ text-align: right;
},
{ "td.timelineGraph",
"the format for the grap placeholder cells in timelines",
@ width: 20;
@ text-align: left;
@ vertical-align: top;
},
{ "a.tagLink",
"the format for the tag links",
@
},
{ "span.tagDsp",
"the format for the tag display(no history permission!)",
@ font-weight: bold;
},
{ "span.wikiError",
"the format for wiki errors",
@ font-weight: bold;
@ color: red;
},
{ "span.infoTagCancelled",
"the format for fixed/canceled tags,..",
@ font-weight: bold;
@ text-decoration: line-through;
},
{ "span.infoTag",
"the format for tags,..",
@ font-weight: bold;
},
{ "span.wikiTagCancelled",
"the format for fixed/cancelled tags,.. on wiki pages",
@ text-decoration: line-through;
},
{ "table.browser",
"format for the file display table",
@ /* the format for wiki errors */
@ width: 100% ;
@ border: 0;
},
{ "td.browser",
"format for cells in the file browser",
@ width: 24% ;
@ vertical-align: top;
},
{ "ul.browser",
"format for the list in the file browser",
@ margin-left: 0.5em;
@ padding-left: 0.5em;
},
{ "table.login_out",
"table format for login/out label/input table",
@ text-align: left;
@ margin-right: 10px;
@ margin-left: 10px;
@ margin-top: 10px;
},
{ "div.captcha",
"captcha display options",
@ text-align: center;
},
{ "table.captcha",
"format for the layout table, used for the captcha display",
@ margin: auto;
@ padding: 10px;
@ outline-width: 1;
@ outline-style: double;
},
{ "td.login_out_label",
"format for the label cells in the login/out table",
@ text-align: center;
},
{ "span.loginError",
"format for login error messages",
@ color: red;
},
{ "span.note",
"format for leading text for notes",
@ font-weight: bold;
},
{ "span.textareaLabel",
"format for textare labels",
@ font-weight: bold;
},
{ "table.usetupLayoutTable",
"format for the user setup layout table",
@ outline-style: none;
@ padding: 0;
@ margin: 25px;
},
{ "td.usetupColumnLayout",
"format of the columns on the user setup list page",
@ vertical-align: top
},
{ "table.usetupUserList",
"format for the user list table on the user setup page",
@ outline-style: double;
@ outline-width: 1;
@ padding: 10px;
},
{ "th.usetupListUser",
"format for table header user in user list on user setup page",
@ text-align: right;
@ padding-right: 20px;
},
{ "th.usetupListCap",
"format for table header capabilities in user list on user setup page",
@ text-align: center;
@ padding-right: 15px;
},
{ "th.usetupListCon",
"format for table header contact info in user list on user setup page",
@ text-align: left;
},
{ "td.usetupListUser",
"format for table cell user in user list on user setup page",
@ text-align: right;
@ padding-right: 20px;
@ white-space:nowrap;
},
{ "td.usetupListCap",
"format for table cell capabilities in user list on user setup page",
@ text-align: center;
@ padding-right: 15px;
},
{ "td.usetupListCon",
"format for table cell contact info in user list on user setup page",
@ text-align: left
},
{ "div.ueditCapBox",
"layout definition for the capabilities box on the user edit detail page",
@ float: left;
@ margin-right: 20px;
@ margin-bottom: 20px;
},
{ "td.usetupEditLabel",
"format of the label cells in the detailed user edit page",
@ text-align: right;
@ vertical-align: top;
@ white-space: nowrap;
},
{ "span.ueditInheritNobody",
"color for capabilities, inherited by nobody",
@ color: green;
},
{ "span.ueditInheritDeveloper",
"color for capabilities, inherited by developer",
@ color: red;
},
{ "span.ueditInheritReader",
"color for capabilities, inherited by reader",
@ color: black;
},
{ "span.ueditInheritAnonymous",
"color for capabilities, inherited by anonymous",
@ color: blue;
},
{ "span.capability",
"format for capabilites, mentioned on the user edit page",
@ font-weight: bold;
},
{ "span.usertype",
"format for different user types, mentioned on the user edit page",
@ font-weight: bold;
},
{ "span.usertype:before",
"leading text for user types, mentioned on the user edit page",
@ content:"'";
},
{ "span.usertype:after",
"trailing text for user types, mentioned on the user edit page",
@ content:"'";
},
{ "span.wikiruleHead",
"format for leading text in wikirules definitions",
@ font-weight: bold;
},
{ "td.tktDspLabel",
"format for labels on ticket display page",
@ text-align: right;
},
{ "td.tktDspValue",
"format for values on ticket display page",
@ text-align: left;
@ vertical-align: top;
@ background-color: #d0d0d0;
},
{ "span.tktError",
"format for ticket error messages",
@ color: red;
@ font-weight: bold;
},
{ 0,
0,
0
}
};
/*
** Append all of the default CSS to the CGI output.
*/
void cgi_append_default_css(void) {
int i;
for (i=0;cssDefaultList[i].elementClass;i++){
if (cssDefaultList[i].elementClass[0]){
cgi_printf("/* %s */\n%s {\n%s\n}\n\n",
cssDefaultList[i].comment,
cssDefaultList[i].elementClass,
cssDefaultList[i].value
);
}else{
cgi_printf("%s",
cssDefaultList[i].value
);
}
}
}
/*
** WEBPAGE: style.css
*/
void page_style_css(void){
const char *zCSS = 0;
int i;
cgi_set_content_type("text/css");
zCSS = db_get("css",(char*)zDefaultCSS);
/* append user defined css */
cgi_append_content(zCSS, -1);
/* add special missing definitions */
for (i=1;cssDefaultList[i].elementClass;i++)
if (!strstr(zCSS,cssDefaultList[i].elementClass)) {
cgi_append_content("/* ", -1);
cgi_append_content(cssDefaultList[i].comment, -1);
cgi_append_content(" */\n", -1);
cgi_append_content(cssDefaultList[i].elementClass, -1);
cgi_append_content(" {\n", -1);
cgi_append_content(cssDefaultList[i].value, -1);
cgi_append_content("}\n\n", -1);
}
g.isConst = 1;
}
/*
** WEBPAGE: test_env
*/
void page_test_env(void){
style_header("Environment Test");
#if !defined(_WIN32)
@ uid=%d(getuid()), gid=%d(getgid())<br />
#endif
@ g.zBaseURL = %h(g.zBaseURL)<br />
@ g.zTop = %h(g.zTop)<br />
cgi_print_all();
style_footer();
}
|
Changes to src/tag.c.
| ︙ | ︙ | |||
512 513 514 515 516 517 518 |
" AND tagname GLOB 'sym-*'"
" ORDER BY tagname"
);
@ <ul>
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
if( g.okHistory ){
| | > | | 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 |
" AND tagname GLOB 'sym-*'"
" ORDER BY tagname"
);
@ <ul>
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
if( g.okHistory ){
@ <li><a class="tagLink" href="%s(g.zBaseURL)/timeline?t=%T(zName)">
@ %h(zName)</a></li>
}else{
@ <li><span class="tagDsp">%h(zName)</span></li>
}
}
@ </ul>
db_finalize(&q);
style_footer();
}
|
| ︙ | ︙ | |||
540 541 542 543 544 545 546 |
" AND tagxref.tagtype>0 AND tagxref.srcid>0"
" AND tag.tagname GLOB 'sym-*'",
rid
);
while( db_step(&q)==SQLITE_ROW ){
const char *zTagName = db_column_text(&q, 0);
if( g.okHistory ){
| | > | | 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 |
" AND tagxref.tagtype>0 AND tagxref.srcid>0"
" AND tag.tagname GLOB 'sym-*'",
rid
);
while( db_step(&q)==SQLITE_ROW ){
const char *zTagName = db_column_text(&q, 0);
if( g.okHistory ){
@ <a class="tagLink" href="%s(g.zBaseURL)/timeline?t=%T(zTagName)">
@ [%h(zTagName)]</a>
}else{
@ <span class="tagDsp">[%h(zTagName)]</span>
}
}
db_finalize(&q);
}
/*
** WEBPAGE: /tagtimeline
|
| ︙ | ︙ | |||
571 572 573 574 575 576 577 |
" AND tagid IN (SELECT tagid FROM tag "
" WHERE tagname GLOB 'sym-*'))"
" ORDER BY event.mtime DESC",
timeline_query_for_www()
);
www_print_timeline(&q, 0, tagtimeline_extra);
db_finalize(&q);
| | | | 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 |
" AND tagid IN (SELECT tagid FROM tag "
" WHERE tagname GLOB 'sym-*'))"
" ORDER BY event.mtime DESC",
timeline_query_for_www()
);
www_print_timeline(&q, 0, tagtimeline_extra);
db_finalize(&q);
@ <br />
@ <script type="text/JavaScript">
@ function xin(id){
@ }
@ function xout(id){
@ }
@ </script>
style_footer();
}
|
Changes to src/th.c.
| ︙ | ︙ | |||
844 845 846 847 848 849 850 |
continue;
}
/* Gobble up input a word at a time until the end of the command
** (a semi-colon or end of line).
*/
while( rc==TH_OK && *zInput!=';' && !thEndOfLine(zInput, nInput) ){
| | | 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 |
continue;
}
/* Gobble up input a word at a time until the end of the command
** (a semi-colon or end of line).
*/
while( rc==TH_OK && *zInput!=';' && !thEndOfLine(zInput, nInput) ){
int nWord=0;
thNextSpace(interp, zInput, nInput, &nSpace);
rc = thNextWord(interp, &zInput[nSpace], nInput-nSpace, &nWord, 1);
zInput += (nSpace+nWord);
nInput -= (nSpace+nWord);
}
if( rc!=TH_OK ) continue;
|
| ︙ | ︙ |
Changes to src/th_main.c.
| ︙ | ︙ | |||
276 277 278 279 280 281 282 |
sendText(z, -1, 0);
free(z);
blob_reset(&name);
for(i=0; i<nElem; i++){
zH = htmlize((char*)azElem[i], aszElem[i]);
if( zValue && aszElem[i]==nValue
&& memcmp(zValue, azElem[i], nValue)==0 ){
| | > | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
sendText(z, -1, 0);
free(z);
blob_reset(&name);
for(i=0; i<nElem; i++){
zH = htmlize((char*)azElem[i], aszElem[i]);
if( zValue && aszElem[i]==nValue
&& memcmp(zValue, azElem[i], nValue)==0 ){
z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>",
zH, zH);
}else{
z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
}
free(zH);
sendText(z, -1, 0);
free(z);
}
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
46 47 48 49 50 51 52 |
/*
** Generate a hyperlink to a version.
*/
void hyperlink_to_uuid(const char *zUuid){
char zShortUuid[UUID_SIZE+1];
shorten_uuid(zShortUuid, zUuid);
if( g.okHistory ){
| | > | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
/*
** Generate a hyperlink to a version.
*/
void hyperlink_to_uuid(const char *zUuid){
char zShortUuid[UUID_SIZE+1];
shorten_uuid(zShortUuid, zUuid);
if( g.okHistory ){
@ <a class="timelineHistLink" href="%s(g.zBaseURL)/info/%s(zShortUuid)">
@ [%s(zShortUuid)]</a>
}else{
@ <span class="timelineHistDsp">[%s(zShortUuid)]</span>
}
}
/*
** Generate a hyperlink that invokes javascript to highlight
** a version on mouseover.
*/
|
| ︙ | ︙ | |||
81 82 83 84 85 86 87 |
** Generate a hyperlink to a diff between two versions.
*/
void hyperlink_to_diff(const char *zV1, const char *zV2){
if( g.okHistory ){
if( zV2==0 ){
@ <a href="%s(g.zBaseURL)/diff?v2=%s(zV1)">[diff]</a>
}else{
| | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
** Generate a hyperlink to a diff between two versions.
*/
void hyperlink_to_diff(const char *zV1, const char *zV2){
if( g.okHistory ){
if( zV2==0 ){
@ <a href="%s(g.zBaseURL)/diff?v2=%s(zV1)">[diff]</a>
}else{
@ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a>
}
}
}
/*
** Generate a hyperlink to a date & time.
*/
|
| ︙ | ︙ | |||
107 108 109 110 111 112 113 |
** events by that user. If the date+time is specified, then the timeline
** is centered on that date+time.
*/
void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){
if( zSuf==0 ) zSuf = "";
if( g.okHistory ){
if( zD && zD[0] ){
| | | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
** events by that user. If the date+time is specified, then the timeline
** is centered on that date+time.
*/
void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){
if( zSuf==0 ) zSuf = "";
if( g.okHistory ){
if( zD && zD[0] ){
@ <a href="%s(g.zTop)/timeline?c=%T(zD)&u=%T(zU)">%h(zU)</a>%s(zSuf)
}else{
@ <a href="%s(g.zTop)/timeline?u=%T(zU)">%h(zU)</a>%s(zSuf)
}
}else{
@ %s(zU)
}
}
|
| ︙ | ︙ | |||
187 188 189 190 191 192 193 194 195 196 |
if( db_get_boolean("timeline-block-markup", 0) ){
wikiFlags = WIKI_INLINE;
}else{
wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
}
if( tmFlags & TIMELINE_GRAPH ){
pGraph = graph_init();
@ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
}
| > > > | | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
if( db_get_boolean("timeline-block-markup", 0) ){
wikiFlags = WIKI_INLINE;
}else{
wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
}
if( tmFlags & TIMELINE_GRAPH ){
pGraph = graph_init();
/* style is not moved to css, because this is
** a technical div for the timeline graph
*/
@ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
}
@ <table class="timelineTable">
blob_zero(&comment);
while( db_step(pQuery)==SQLITE_ROW ){
int rid = db_column_int(pQuery, 0);
const char *zUuid = db_column_text(pQuery, 1);
int isLeaf = db_column_int(pQuery, 5);
const char *zBgClr = db_column_text(pQuery, 6);
const char *zDate = db_column_text(pQuery, 2);
|
| ︙ | ︙ | |||
216 217 218 219 220 221 222 |
}else{
commentColumn = 10;
}
}
}
prevTagid = tagid;
if( suppressCnt ){
| | | | | | | | | 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 |
}else{
commentColumn = 10;
}
}
}
prevTagid = tagid;
if( suppressCnt ){
@ <tr><td /><td /><td>
@ <span class="timelineDisabled">... %d(suppressCnt) similar
@ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr>
suppressCnt = 0;
}
if( strcmp(zType,"div")==0 ){
@ <tr><td colspan="3"><hr /></td></tr>
continue;
}
if( memcmp(zDate, zPrevDate, 10) ){
sprintf(zPrevDate, "%.10s", zDate);
@ <tr><td>
@ <div class="divider">%s(zPrevDate)</div>
@ </td></tr>
}
memcpy(zTime, &zDate[11], 5);
zTime[5] = 0;
@ <tr>
@ <td class="timelineTime">%s(zTime)</td>
@ <td class="timelineGraph">
if( pGraph && zType[0]=='c' ){
int nParent = 0;
int aParent[32];
const char *zBr;
int gidx;
static Stmt qparent;
static Stmt qbranch;
|
| ︙ | ︙ | |||
265 266 267 268 269 270 271 272 |
}else{
zBr = "trunk";
}
gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr);
db_reset(&qbranch);
@ <div id="m%d(gidx)"></div>
}
if( zBgClr && zBgClr[0] ){
| > | | | | | 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 |
}else{
zBr = "trunk";
}
gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr);
db_reset(&qbranch);
@ <div id="m%d(gidx)"></div>
}
@</td>
if( zBgClr && zBgClr[0] ){
@ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
}else{
@ <td class="timelineTableCell">
}
if( zType[0]=='c' ){
hyperlink_to_uuid(zUuid);
if( isLeaf ){
if( db_exists("SELECT 1 FROM tagxref"
" WHERE rid=%d AND tagid=%d AND tagtype>0",
rid, TAG_CLOSED) ){
@ <span class="timelineLeaf">Closed-Leaf:</span>
}else{
@ <span class="timelineLeaf">Leaf:</span>
}
}
}else if( (tmFlags & TIMELINE_ARTID)!=0 ){
hyperlink_to_uuid(zUuid);
}
db_column_blob(pQuery, commentColumn, &comment);
if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
|
| ︙ | ︙ | |||
307 308 309 310 311 312 313 |
}
if( xExtra ){
xExtra(rid);
}
@ </td></tr>
}
if( suppressCnt ){
| | | | > > > > | > | | | | 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 |
}
if( xExtra ){
xExtra(rid);
}
@ </td></tr>
}
if( suppressCnt ){
@ <tr><td /><td /><td>
@ <span class="timelineDisabled">... %d(suppressCnt) similar
@ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr>
suppressCnt = 0;
}
if( pGraph ){
graph_finish(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0);
if( pGraph->nErr ){
graph_free(pGraph);
pGraph = 0;
}else{
/* style is not moved to css, because this is
** a technical div for the timeline graph
*/
@ <tr><td /><td>
@ <div id="grbtm" style="width:%d(pGraph->mxRail*20+30)px;"></div>
@ </td></tr>
}
}
@ </table>
timeline_output_graph_javascript(pGraph);
}
/*
** Generate all of the necessary javascript to generate a timeline
** graph.
*/
void timeline_output_graph_javascript(GraphContext *pGraph){
if( pGraph && pGraph->nErr==0 ){
GraphRow *pRow;
int i;
char cSep;
@ <script type="text/JavaScript">
cgi_printf("var rowinfo = [\n");
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
cgi_printf("{id:\"m%d\",bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:",
pRow->idx,
pRow->zBgClr,
pRow->iRail,
pRow->bDescender,
pRow->mergeOut,
pRow->mergeUpto,
pRow->aiRaiser[pRow->iRail]
);
cSep = '[';
for(i=0; i<GR_MAX_RAIL; i++){
if( i==pRow->iRail ) continue;
if( pRow->aiRaiser[i]>0 ){
cgi_printf("%c%d,%d", cSep, i, pRow->aiRaiser[i]);
cSep = ',';
}
}
if( cSep=='[' ) cgi_printf("[");
cgi_printf("],mi:");
cSep = '[';
for(i=0; i<GR_MAX_RAIL; i++){
if( pRow->mergeIn & (1<<i) ){
cgi_printf("%c%d", cSep, i);
cSep = ',';
}
}
if( cSep=='[' ) cgi_printf("[");
cgi_printf("]}%s", pRow->pNext ? ",\n" : "];\n");
}
cgi_printf("var nrail = %d\n", pGraph->mxRail+1);
|
| ︙ | ︙ | |||
480 481 482 483 484 485 486 |
@ var canvasY = absoluteY("canvas");
@ var left = absoluteX(rowinfo[0].id) - absoluteX("canvas") + 15;
@ var width = nrail*20;
@ for(var i in rowinfo){
@ rowinfo[i].y = absoluteY(rowinfo[i].id) + 10 - canvasY;
@ rowinfo[i].x = left + rowinfo[i].r*20;
@ }
| | | 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 |
@ var canvasY = absoluteY("canvas");
@ var left = absoluteX(rowinfo[0].id) - absoluteX("canvas") + 15;
@ var width = nrail*20;
@ for(var i in rowinfo){
@ rowinfo[i].y = absoluteY(rowinfo[i].id) + 10 - canvasY;
@ rowinfo[i].x = left + rowinfo[i].r*20;
@ }
@ var btm = absoluteY("grbtm") + 10 - canvasY;
@ if( btm<32768 ){
@ canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+
@ 'style="position:absolute;left:'+(left-5)+'px;"' +
@ ' width="'+width+'" height="'+btm+'"></canvas>';
@ realCanvas = document.getElementById('timeline-canvas');
@ }else{
@ realCanvas = 0;
|
| ︙ | ︙ | |||
880 881 882 883 884 885 886 |
blob_appendf(&desc, " tagged with \"%h\"", zTagName);
tmFlags |= TIMELINE_DISJOINT;
}else if( zBrName ){
blob_appendf(&desc, " related to \"%h\"", zBrName);
tmFlags |= TIMELINE_DISJOINT;
}
if( zAfter ){
| | | | | 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 |
blob_appendf(&desc, " tagged with \"%h\"", zTagName);
tmFlags |= TIMELINE_DISJOINT;
}else if( zBrName ){
blob_appendf(&desc, " related to \"%h\"", zBrName);
tmFlags |= TIMELINE_DISJOINT;
}
if( zAfter ){
blob_appendf(&desc, " occurring on or after %h.<br />", zAfter);
}else if( zBefore ){
blob_appendf(&desc, " occurring on or before %h.<br />", zBefore);
}else if( zCirca ){
blob_appendf(&desc, " occurring around %h.<br />", zCirca);
}
if( zSearch ){
blob_appendf(&desc, " matching \"%h\"", zSearch);
}
if( g.okHistory ){
if( zAfter || n==nEntry ){
zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
|
| ︙ | ︙ |
Changes to src/tkt.c.
| ︙ | ︙ | |||
311 312 313 314 315 316 317 |
}
if( g.okNewTkt ){
style_submenu_element("New Ticket", "Create a new ticket",
"%s/tktnew", g.zTop);
}
if( g.okApndTkt && g.okAttach ){
style_submenu_element("Attach", "Add An Attachment",
| | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
}
if( g.okNewTkt ){
style_submenu_element("New Ticket", "Create a new ticket",
"%s/tktnew", g.zTop);
}
if( g.okApndTkt && g.okAttach ){
style_submenu_element("Attach", "Add An Attachment",
"%s/attachadd?tkt=%T&from=%s/tktview/%t",
g.zTop, zUuid, g.zTop, zUuid);
}
style_header("View Ticket");
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
ticket_init();
initializeVariablesFromDb();
zScript = ticket_viewpage_code();
|
| ︙ | ︙ | |||
340 341 342 343 344 345 346 |
" ORDER BY mtime DESC",
zFullName);
while( db_step(&q)==SQLITE_ROW ){
const char *zDate = db_column_text(&q, 0);
const char *zFile = db_column_text(&q, 1);
const char *zUser = db_column_text(&q, 2);
if( cnt==0 ){
| | > | | > > > > | | 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 |
" ORDER BY mtime DESC",
zFullName);
while( db_step(&q)==SQLITE_ROW ){
const char *zDate = db_column_text(&q, 0);
const char *zFile = db_column_text(&q, 1);
const char *zUser = db_column_text(&q, 2);
if( cnt==0 ){
@ <hr /><h2>Attachments:</h2>
@ <ul>
}
cnt++;
if( g.okRead && g.okHistory ){
@ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)">
@ %h(zFile)</a>
}else{
@ %h(zFile)
}
@ added by %h(zUser) on
hyperlink_to_date(zDate, ".");
if( g.okWrTkt && g.okAttach ){
@ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&file=%t(zFile)&from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
}
}
if( cnt ){
@ </ul>
}
db_finalize(&q);
}
|
| ︙ | ︙ | |||
504 505 506 507 508 509 510 |
}
style_header("New Ticket");
if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
ticket_init();
getAllTicketFields();
initializeVariablesFromDb();
initializeVariablesFromCGI();
| | > | 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 |
}
style_header("New Ticket");
if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
ticket_init();
getAllTicketFields();
initializeVariablesFromDb();
initializeVariablesFromCGI();
@ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><p>
login_insert_csrf_secret();
@ </p>
zScript = ticket_newpage_code();
Th_Store("login", g.zLogin);
Th_Store("date", db_text(0, "SELECT datetime('now')"));
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
(void*)&zNewUuid, 0);
if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1);
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
|
| ︙ | ︙ | |||
547 548 549 550 551 552 553 |
zName = P("name");
if( P("cancel") ){
cgi_redirectf("tktview?name=%T", zName);
}
style_header("Edit Ticket");
if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE
|| !validate16(zName,nName) ){
| | | | > | | > | 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 |
zName = P("name");
if( P("cancel") ){
cgi_redirectf("tktview?name=%T", zName);
}
style_header("Edit Ticket");
if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE
|| !validate16(zName,nName) ){
@ <span class="tktError">Not a valid ticket id: \"%h(zName)\"</span>
style_footer();
return;
}
nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'",
zName);
if( nRec==0 ){
@ <span class="tktError">No such ticket: \"%h(zName)\"</span>
style_footer();
return;
}
if( nRec>1 ){
@ <span class="tktError"><b>%d(nRec) tickets begin with:
@ \"%h(zName)\"</span>
style_footer();
return;
}
if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
ticket_init();
getAllTicketFields();
initializeVariablesFromCGI();
initializeVariablesFromDb();
@ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><p>
@ <input type="hidden" name="name" value="%s(zName)" />
login_insert_csrf_secret();
@ </p>
zScript = ticket_editpage_code();
Th_Store("login", g.zLogin);
Th_Store("date", db_text(0, "SELECT datetime('now')"));
Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1);
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
|
| ︙ | ︙ | |||
617 618 619 620 621 622 623 |
}
}
return 0;
}
/*
** WEBPAGE: tkttimeline
| | | | 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 |
}
}
return 0;
}
/*
** WEBPAGE: tkttimeline
** URL: /tkttimeline?name=TICKETUUID&y=TYPE
**
** Show the change history for a single ticket in timeline format.
*/
void tkttimeline_page(void){
Stmt q;
char *zTitle;
char *zSQL;
const char *zUuid;
char *zFullUuid;
int tagid;
char zGlobPattern[50];
const char *zType;
login_check_credentials();
if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
zUuid = PD("name","");
zType = PD("y","a");
if( zType[0]!='c' ){
style_submenu_element("Check-ins", "Check-ins",
"%s/tkttimeline?name=%T&y=ci", g.zTop, zUuid);
}else{
style_submenu_element("Timeline", "Timeline",
"%s/tkttimeline?name=%T", g.zTop, zUuid);
}
style_submenu_element("History", "History",
"%s/tkthistory/%s", g.zTop, zUuid);
style_submenu_element("Status", "Status",
|
| ︙ | ︙ | |||
769 770 771 772 773 774 775 |
if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
@
@ <p>Ticket change
@ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
@ (rid %d(rid)) by
hyperlink_to_user(m.zUser,zDate," on");
hyperlink_to_date(zDate, ":");
| < > | 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 |
if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
@
@ <p>Ticket change
@ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
@ (rid %d(rid)) by
hyperlink_to_user(m.zUser,zDate," on");
hyperlink_to_date(zDate, ":");
@ </p>
ticket_output_change_artifact(&m);
}
manifest_clear(&m);
}
}
db_finalize(&q);
style_footer();
}
|
| ︙ | ︙ |
Changes to src/tktsetup.c.
| ︙ | ︙ | |||
128 129 130 131 132 133 134 |
@ <p><font color="red"><b>ERROR: %h(zErr)</b></font></p>
}else{
db_set(zDbField, z, 0);
if( xRebuild ) xRebuild();
cgi_redirect("tktsetup");
}
}
| | | | | | | | | | | | 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 |
@ <p><font color="red"><b>ERROR: %h(zErr)</b></font></p>
}else{
db_set(zDbField, z, 0);
if( xRebuild ) xRebuild();
cgi_redirect("tktsetup");
}
}
@ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="post"><div>
login_insert_csrf_secret();
@ <p>%s(zDesc)</p>
@ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
@ <blockquote><p>
@ <input type="submit" name="submit" value="Apply Changes" />
@ <input type="submit" name="clear" value="Revert To Default" />
@ <input type="submit" name="setup" value="Cancel" />
@ </p></blockquote>
@ </div></form>
@ <hr />
@ <h2>Default %s(zTitle)</h2>
@ <blockquote><pre>
@ %h(zDfltValue)
@ </pre></blockquote>
style_footer();
}
/*
** WEBPAGE: tktsetup_tab
*/
void tktsetup_tab_page(void){
static const char zDesc[] =
@ Enter a valid CREATE TABLE statement for the "ticket" table. The
@ table must contain columns named "tkt_id", "tkt_uuid", and "tkt_mtime"
@ with an unique index on "tkt_uuid" and "tkt_mtime".
;
tktsetup_generic(
"Ticket Table Schema",
"ticket-table",
zDefaultTicketTable,
zDesc,
ticket_schema_check,
|
| ︙ | ︙ | |||
227 228 229 230 231 232 233 |
}
/*
** WEBPAGE: tktsetup_com
*/
void tktsetup_com_page(void){
static const char zDesc[] =
| | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | 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 |
}
/*
** WEBPAGE: tktsetup_com
*/
void tktsetup_com_page(void){
static const char zDesc[] =
@ Enter TH1 script that initializes variables prior to generating
@ any of the ticket view, edit, or creation pages.
;
tktsetup_generic(
"Ticket Common Script",
"ticket-common",
zDefaultTicketCommon,
zDesc,
0,
0,
30
);
}
static const char zDefaultNew[] =
@ <th1>
@ if {[info exists submit]} {
@ set status Open
@ submit_ticket
@ }
@ </th1>
@ <h1 style="text-align: center;">Enter A New Ticket</h1>
@ <table cellpadding="5">
@ <tr>
@ <td colspan="2">
@ Enter a one-line summary of the ticket:<br />
@ <input type="text" name="title" size="60" value="$<title>" />
@ </td>
@ </tr>
@
@ <tr>
@ <td style="text-align: center;">Type:
@ <th1>combobox type $type_choices 1</th1>
@ </td>
@ <td>What type of ticket is this?</td>
@ </tr>
@
@ <tr>
@ <td style="text-align: center;">Version:
@ <input type="text" name="foundin" size="20" value="$<foundin>" />
@ </td>
@ <td>In what version or build number do you observe the problem?</td>
@ </tr>
@
@ <tr>
@ <td style="text-align: center;">Severity:
@ <th1>combobox severity $severity_choices 1</th1>
@ </td>
@ <td>How debilitating is the problem? How badly does the problem
@ affect the operation of the product?</td>
@ </tr>
@
@ <tr>
@ <td style="text-align: center;">EMail:
@ <input type="text" name="private_contact" value="$<private_contact>" size="30" />
@ </td>
@ <td><span style="text-decoration: underline;">Not publicly visible</span>.
@ Used by developers to contact you with questions.</td>
@ </tr>
@
@ <tr>
@ <td colspan="2">
@ Enter a detailed description of the problem.
@ For code defects, be sure to provide details on exactly how
@ the problem can be reproduced. Provide as much detail as
@ possible.
@ <br />
@ <th1>set nline [linecount $comment 50 10]</th1>
@ <textarea name="comment" cols="80" rows="$nline"
@ wrap="virtual" class="wikiedit">$<comment></textarea><br />
@ <input type="submit" name="preview" value="Preview" /></td>
@ </tr>
@
@ <th1>enable_output [info exists preview]</th1>
@ <tr><td colspan="2">
@ Description Preview:<br /><hr />
@ <th1>wiki $comment</th1>
@ <hr />
@ </td></tr>
@ <th1>enable_output 1</th1>
@
@ <tr>
@ <td style="text-align: center;">
@ <input type="submit" name="submit" value="Submit" />
@ </td>
@ <td>After filling in the information above, press this button to create
@ the new ticket</td>
@ </tr>
@ <tr>
@ <td style="text-align: center;">
@ <input type="submit" name="cancel" value="Cancel" />
@ </td>
@ <td>Abandon and forget this ticket</td>
@ </tr>
@ </table>
;
/*
** Return the code used to generate the new ticket page
*/
const char *ticket_newpage_code(void){
return db_get("ticket-newpage", (char*)zDefaultNew);
}
/*
** WEBPAGE: tktsetup_newpage
*/
void tktsetup_newpage_page(void){
static const char zDesc[] =
@ Enter HTML with embedded TH1 script that will render the "new ticket"
@ page
;
tktsetup_generic(
"HTML For New Tickets",
"ticket-newpage",
zDefaultNew,
zDesc,
0,
0,
40
);
}
static const char zDefaultView[] =
@ <table cellpadding="5">
@ <tr><td class="tktDspLabel">Ticket UUID:</td>
@ <td class="tktDspValue" colspan="3">$<tkt_uuid></td></tr>
@ <tr><td class="tktDspLabel">Title:</td>
@ <td class="tktDspValue" colspan="3">
@ <th1>wiki $title</th1>
@ </td></tr>
@ <tr><td class="tktDspLabel">Status:</td><td class="tktDspValue">
@ $<status>
@ </td>
@ <td class="tktDspLabel">Type:</td><td class="tktDspValue">
@ $<type>
@ </td></tr>
@ <tr><td class="tktDspLabel">Severity:</td><td class="tktDspValue">
@ $<severity>
@ </td>
@ <td class="tktDspLabel">Priority:</td><td class="tktDspValue">
@ $<priority>
@ </td></tr>
@ <tr><td class="tktDspLabel">Subsystem:</td><td class="tktDspValue">
@ $<subsystem>
@ </td>
@ <td class="tktDspLabel">Resolution:</td><td class="tktDspValue">
@ $<resolution>
@ </td></tr>
@ <tr><td class="tktDspLabel">Last Modified:</td><td class="tktDspValue">
@ $<tkt_datetime>
@ </td>
@ <th1>enable_output [hascap e]</th1>
@ <td class="tktDspLabel">Contact:</td><td class="tktDspValue">
@ $<private_contact>
@ </td>
@ <th1>enable_output 1</th1>
@ </tr>
@ <tr><td class="tktDspLabel">Version Found In:</td>
@ <td colspan="3" valign="top" class="tktDspValue">
@ $<foundin>
@ </td></tr>
@ <tr><td>Description & Comments:</td></tr>
@ <tr><td colspan="4" class="tktDspValue">
@ <th1>wiki $comment</th1>
@ </td></tr>
@ </table>
;
/*
** Return the code used to generate the view ticket page
*/
const char *ticket_viewpage_code(void){
return db_get("ticket-viewpage", (char*)zDefaultView);
}
/*
** WEBPAGE: tktsetup_viewpage
*/
void tktsetup_viewpage_page(void){
static const char zDesc[] =
@ Enter HTML with embedded TH1 script that will render the "view ticket" page
;
tktsetup_generic(
"HTML For Viewing Tickets",
"ticket-viewpage",
zDefaultView,
zDesc,
0,
0,
40
);
}
static const char zDefaultEdit[] =
@ <th1>
@ if {![info exists username]} {set username $login}
@ if {[info exists submit]} {
@ if {[info exists cmappnd]} {
@ if {[string length $cmappnd]>0} {
@ set ctxt "\n\n<hr /><i>[htmlize $login]"
@ if {$username ne $login} {
@ set ctxt "$ctxt claiming to be [htmlize $username]"
@ }
@ set ctxt "$ctxt added on [date]:</i><br />\n$cmappnd"
@ append_field comment $ctxt
@ }
@ }
@ submit_ticket
@ }
@ </th1>
@ <table cellpadding="5">
@ <tr><td class="tktDspLabel">Title:</td><td>
@ <input type="text" name="title" value="$<title>" size="60" />
@ </td></tr>
@ <tr><td class="tktDspLabel">Status:</td><td>
@ <th1>combobox status $status_choices 1</th1>
@ </td></tr>
@ <tr><td class="tktDspLabel">Type:</td><td>
@ <th1>combobox type $type_choices 1</th1>
@ </td></tr>
@ <tr><td class="tktDspLabel">Severity:</td><td>
@ <th1>combobox severity $severity_choices 1</th1>
@ </td></tr>
@ <tr><td class="tktDspLabel">Priority:</td><td>
@ <th1>combobox priority $priority_choices 1</th1>
@ </td></tr>
@ <tr><td class="tktDspLabel">Resolution:</td><td>
@ <th1>combobox resolution $resolution_choices 1</th1>
@ </td></tr>
@ <tr><td class="tktDspLabel">Subsystem:</td><td>
@ <th1>combobox subsystem $subsystem_choices 1</th1>
@ </td></tr>
@ <th1>enable_output [hascap e]</th1>
@ <tr><td class="tktDspLabel">Contact:</td><td>
@ <input type="text" name="private_contact" size="40"
@ value="$<private_contact>" />
@ </td></tr>
@ <th1>enable_output 1</th1>
@ <tr><td class="tktDspLabel">Version Found In:</td><td>
@ <input type="text" name="foundin" size="50" value="$<foundin>" />
@ </td></tr>
@ <tr><td colspan="2">
@ <th1>
@ if {![info exists eall]} {set eall 0}
@ if {[info exists aonlybtn]} {set eall 0}
@ if {[info exists eallbtn]} {set eall 1}
@ if {![hascap w]} {set eall 0}
@ if {![info exists cmappnd]} {set cmappnd {}}
@ set nline [linecount $comment 15 10]
@ enable_output $eall
@ </th1>
@ Description And Comments:<br />
@ <textarea name="comment" cols="80" rows="$nline"
@ wrap="virtual" class="wikiedit">$<comment></textarea><br />
@ <input type="hidden" name="eall" value="1" />
@ <input type="submit" name="aonlybtn" value="Append Remark" />
@ <input type="submit" name="preview1btn" value="Preview" />
@ <th1>enable_output [expr {!$eall}]</th1>
@ Append Remark from
@ <input type="text" name="username" value="$<username>" size="30" />:<br />
@ <textarea name="cmappnd" cols="80" rows="15"
@ wrap="virtual" class="wikiedit">$<cmappnd></textarea><br />
@ <th1>enable_output [expr {[hascap w] && !$eall}]</th1>
@ <input type="submit" name="eallbtn" value="Edit All" />
@ <th1>enable_output [expr {!$eall}]</th1>
@ <input type="submit" name="preview2btn" value="Preview" />
@ <th1>enable_output 1</th1>
@ </td></tr>
@
@ <th1>enable_output [info exists preview1btn]</th1>
@ <tr><td colspan="2">
@ Description Preview:<br /><hr />
@ <th1>wiki $comment</th1>
@ <hr />
@ </td></tr>
@ <th1>enable_output [info exists preview2btn]</th1>
@ <tr><td colspan="2">
@ Description Preview:<br /><hr />
@ <th1>wiki $cmappnd</th1>
@ <hr />
@ </td></tr>
@ <th1>enable_output 1</th1>
@
@ <tr><td align="right"></td><td>
@ <input type="submit" name="submit" value="Submit Changes" />
@ <input type="submit" name="cancel" value="Cancel" />
@ </td></tr>
@ </table>
;
/*
** Return the code used to generate the edit ticket page
*/
const char *ticket_editpage_code(void){
return db_get("ticket-editpage", (char*)zDefaultEdit);
}
/*
** WEBPAGE: tktsetup_editpage
*/
void tktsetup_editpage_page(void){
static const char zDesc[] =
@ Enter HTML with embedded TH1 script that will render the "edit ticket" page
;
tktsetup_generic(
"HTML For Editing Tickets",
"ticket-editpage",
zDefaultEdit,
zDesc,
0,
|
| ︙ | ︙ | |||
583 584 585 586 587 588 589 |
}
/*
** WEBPAGE: tktsetup_reportlist
*/
void tktsetup_reportlist(void){
static const char zDesc[] =
| | < | 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 |
}
/*
** WEBPAGE: tktsetup_reportlist
*/
void tktsetup_reportlist(void){
static const char zDesc[] =
@ Enter HTML with embedded TH1 script that will render the "report list" page
;
tktsetup_generic(
"HTML For Report List",
"ticket-reportlist",
zDefaultReportList,
zDesc,
0,
|
| ︙ | ︙ | |||
631 632 633 634 635 636 637 |
}
/*
** WEBPAGE: tktsetup_rpttplt
*/
void tktsetup_rpttplt_page(void){
static const char zDesc[] =
| | | | 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 |
}
/*
** WEBPAGE: tktsetup_rpttplt
*/
void tktsetup_rpttplt_page(void){
static const char zDesc[] =
@ Enter the default ticket report format template. This is the
@ the template report format that initially appears when creating a
@ new ticket summary report.
;
tktsetup_generic(
"Default Report Template",
"ticket-report-template",
zDefaultReport,
zDesc,
0,
|
| ︙ | ︙ | |||
672 673 674 675 676 677 678 |
}
/*
** WEBPAGE: tktsetup_keytplt
*/
void tktsetup_keytplt_page(void){
static const char zDesc[] =
| | | | 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 |
}
/*
** WEBPAGE: tktsetup_keytplt
*/
void tktsetup_keytplt_page(void){
static const char zDesc[] =
@ Enter the default ticket report color-key template. This is the
@ the color-key that initially appears when creating a
@ new ticket summary report.
;
tktsetup_generic(
"Default Report Color-Key Template",
"ticket-key-template",
zDefaultKey,
zDesc,
0,
|
| ︙ | ︙ | |||
701 702 703 704 705 706 707 |
}
if( P("setup") ){
cgi_redirect("tktsetup");
}
style_header("Ticket Display On Timelines");
db_begin_transaction();
| | | | | | | | | | 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 |
}
if( P("setup") ){
cgi_redirect("tktsetup");
}
style_header("Ticket Display On Timelines");
db_begin_transaction();
@ <form action="%s(g.zBaseURL)/tktsetup_timeline" method="post"><div>
login_insert_csrf_secret();
@ <hr />
entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title");
@ <p>An SQL expression in a query against the TICKET table that will
@ return the title of the ticket for display purposes.</p>
@ <hr />
entry_attribute("Ticket Status", 40, "ticket-status-column", "s", "status");
@ <p>The name of the column in the TICKET table that contains the ticket
@ status in human-readable form. Case sensitive.</p>
@ <hr />
entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c",
"status='Closed'");
@ <p>An SQL expression that evaluates to true in a TICKET table query if
@ the ticket is closed.</p>
@ <hr />
@ <p>
@ <input type="submit" name="submit" value="Apply Changes" />
@ <input type="submit" name="setup" value="Cancel" />
@ </p>
@ </div></form>
db_end_transaction(0);
style_footer();
}
|
Changes to src/translate.c.
| ︙ | ︙ | |||
64 65 66 67 68 69 70 |
}
}
/*
** Translate the input stream into the output stream
*/
static void trans(FILE *in, FILE *out){
| | | | > | | | > | > | | 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 |
}
}
/*
** Translate the input stream into the output stream
*/
static void trans(FILE *in, FILE *out){
int i, j, k; /* Loop counters */
char c1, c2; /* Characters used to start a comment */
int lastWasEq = 0; /* True if last non-whitespace character was "=" */
int lastWasComma = 0; /* True if last non-whitespace character was "," */
char zLine[2000]; /* A single line of input */
char zOut[4000]; /* The input line translated into appropriate output */
c1 = c2 = '-';
while( fgets(zLine, sizeof(zLine), in) ){
for(i=0; zLine[i] && isspace(zLine[i]); i++){}
if( zLine[i]!='@' ){
if( inPrint || inStr ) end_block(out);
fprintf(out,"%s",zLine);
/* 0123456789 12345 */
if( strncmp(zLine, "/* @-comment: ", 14)==0 ){
c1 = zLine[14];
c2 = zLine[15];
}
i += strlen(&zLine[i]);
while( i>0 && isspace(zLine[i-1]) ){ i--; }
lastWasEq = i>0 && zLine[i-1]=='=';
lastWasComma = i>0 && zLine[i-1]==',';
}else if( lastWasEq || lastWasComma){
/* If the last non-whitespace character before the first @ was
** an "="(var init/set) or a ","(const definition in list) then
** generate a string literal. But skip comments
** consisting of all text between c1 and c2 (default "--")
** and end of line.
*/
int indent, omitline;
i++;
if( isspace(zLine[i]) ){ i++; }
indent = i - 2;
|
| ︙ | ︙ |
Changes to src/update.c.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 |
#include "update.h"
#include <assert.h>
/*
** Return true if artifact rid is a version
*/
int is_a_version(int rid){
| | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include "update.h"
#include <assert.h>
/*
** Return true if artifact rid is a version
*/
int is_a_version(int rid){
return db_exists("SELECT 1 FROM event WHERE objid=%d AND type='ci'", rid);
}
/*
** COMMAND: update
**
** Usage: %fossil update ?VERSION? ?FILES...?
**
|
| ︙ | ︙ | |||
109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
fossil_fatal("Multiple descendants");
}
tid = db_int(0, "SELECT rid FROM leaves, event"
" WHERE event.objid=leaves.rid"
" ORDER BY event.mtime DESC");
}
db_begin_transaction();
vfile_check_signature(vid, 1);
if( !nochangeFlag ) undo_begin();
load_vfile_from_rid(tid);
/*
** The record.fn field is used to match files against each other. The
| > | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
fossil_fatal("Multiple descendants");
}
tid = db_int(0, "SELECT rid FROM leaves, event"
" WHERE event.objid=leaves.rid"
" ORDER BY event.mtime DESC");
}
if( tid==vid ) return; /* Nothing to update */
db_begin_transaction();
vfile_check_signature(vid, 1);
if( !nochangeFlag ) undo_begin();
load_vfile_from_rid(tid);
/*
** The record.fn field is used to match files against each other. The
|
| ︙ | ︙ |
Changes to src/url.c.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** ******************************************************************************* ** ** This file contains code for parsing URLs that appear on the command-line */ #include "config.h" #include "url.h" /* ** Parse the given URL. Populate variables in the global "g" structure. ** ** g.urlIsFile True if FILE: ** g.urlIsHttps True if HTTPS: ** g.urlProtocol "http" or "https" or "file" | > > > > > > > > > > > | | > > > > | > > > > > > > > > > > > > > > > > < | > | > > > > > > > > > > > > > > > > > > > > > > > | > > > > | | | | > | 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 |
**
*******************************************************************************
**
** This file contains code for parsing URLs that appear on the command-line
*/
#include "config.h"
#include "url.h"
/*
** Convert a string to lower-case.
*/
static void url_tolower(char *z){
while( *z ){
*z = tolower(*z);
z++;
}
}
/*
** Parse the given URL. Populate variables in the global "g" structure.
**
** g.urlIsFile True if FILE:
** g.urlIsHttps True if HTTPS:
** g.urlIsSsh True if SSH:
** g.urlProtocol "http" or "https" or "file"
** g.urlName Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE:
** g.urlPort TCP port number for HTTP or HTTPS.
** g.urlDfltPort Default TCP port number (80 or 443).
** g.urlPath Path name for HTTP or HTTPS.
** g.urlUser Userid.
** g.urlPasswd Password.
** g.urlHostname HOST:PORT or just HOST if port is the default.
** g.urlCanonical The URL in canonical form, omitting the password
**
** HTTP url format is:
**
** http://userid:password@host:port/path
**
** SSH url format is:
**
** ssh://userid:password@host:port/path?fossil=path/to/fossil.exe
**
*/
void url_parse(const char *zUrl){
int i, j, c;
char *zFile = 0;
if( strncmp(zUrl, "http://", 7)==0
|| strncmp(zUrl, "https://", 8)==0
|| strncmp(zUrl, "ssh://", 6)==0
){
int iStart;
char *zLogin;
char *zExe;
g.urlIsFile = 0;
if( zUrl[4]=='s' ){
g.urlIsHttps = 1;
g.urlProtocol = "https";
g.urlDfltPort = 443;
iStart = 8;
}else if( zUrl[0]=='s' ){
g.urlIsSsh = 1;
g.urlProtocol = "ssh";
g.urlDfltPort = 22;
g.urlFossil = "fossil";
iStart = 6;
}else{
g.urlIsHttps = 0;
g.urlProtocol = "http";
g.urlDfltPort = 80;
iStart = 7;
}
for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){}
if( c=='@' ){
/* Parse up the user-id and password */
for(j=iStart; j<i && zUrl[j]!=':'; j++){}
g.urlUser = mprintf("%.*s", j-iStart, &zUrl[iStart]);
dehttpize(g.urlUser);
if( j<i ){
g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]);
dehttpize(g.urlPasswd);
}
if( g.urlIsSsh && g.urlPasswd ){
zLogin = mprintf("%t:*@", g.urlUser);
}else{
zLogin = mprintf("%t@", g.urlUser);
}
for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){}
g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]);
i = j;
}else{
for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){}
g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]);
zLogin = mprintf("");
}
url_tolower(g.urlName);
if( c==':' ){
g.urlPort = 0;
i++;
while( (c = zUrl[i])!=0 && isdigit(c) ){
g.urlPort = g.urlPort*10 + c - '0';
i++;
}
g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort);
}else{
g.urlPort = g.urlDfltPort;
g.urlHostname = g.urlName;
}
dehttpize(g.urlName);
g.urlPath = mprintf("%s", &zUrl[i]);
for(i=0; g.urlPath[i] && g.urlPath[i]!='?'; i++){}
if( g.urlPath[i] ){
g.urlPath[i] = 0;
i++;
}
zExe = mprintf("");
while( g.urlPath[i]!=0 ){
char *zName, *zValue;
zName = &g.urlPath[i];
zValue = zName;
while( g.urlPath[i] && g.urlPath[i]!='=' ){ i++; }
if( g.urlPath[i]=='=' ){
g.urlPath[i] = 0;
i++;
zValue = &g.urlPath[i];
while( g.urlPath[i] && g.urlPath[i]!='&' ){ i++; }
}
if( g.urlPath[i] ){
g.urlPath[i] = 0;
i++;
}
if( strcmp(zName,"fossil")==0 ){
g.urlFossil = zValue;
dehttpize(g.urlFossil);
zExe = mprintf("?fossil=%T", g.urlFossil);
}
}
dehttpize(g.urlPath);
if( g.urlDfltPort==g.urlPort ){
g.urlCanonical = mprintf(
"%s://%s%T%T%s",
g.urlProtocol, zLogin, g.urlName, g.urlPath, zExe
);
}else{
g.urlCanonical = mprintf(
"%s://%s%T:%d%T%s",
g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath, zExe
);
}
if( g.urlIsSsh && g.urlPath[1] ) g.urlPath++;
free(zLogin);
}else if( strncmp(zUrl, "file:", 5)==0 ){
g.urlIsFile = 1;
if( zUrl[5]=='/' && zUrl[6]=='/' ){
i = 7;
}else{
i = 5;
|
| ︙ | ︙ | |||
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
if( g.argc!=3 && g.argc!=4 ){
usage("URL");
}
url_parse(g.argv[2]);
for(i=0; i<2; i++){
printf("g.urlIsFile = %d\n", g.urlIsFile);
printf("g.urlIsHttps = %d\n", g.urlIsHttps);
printf("g.urlProtocol = %s\n", g.urlProtocol);
printf("g.urlName = %s\n", g.urlName);
printf("g.urlPort = %d\n", g.urlPort);
printf("g.urlDfltPort = %d\n", g.urlDfltPort);
printf("g.urlHostname = %s\n", g.urlHostname);
printf("g.urlPath = %s\n", g.urlPath);
printf("g.urlUser = %s\n", g.urlUser);
printf("g.urlPasswd = %s\n", g.urlPasswd);
printf("g.urlCanonical = %s\n", g.urlCanonical);
if( i==0 ){
printf("********\n");
url_enable_proxy("Using proxy: ");
}
}
}
| > > > | 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 |
if( g.argc!=3 && g.argc!=4 ){
usage("URL");
}
url_parse(g.argv[2]);
for(i=0; i<2; i++){
printf("g.urlIsFile = %d\n", g.urlIsFile);
printf("g.urlIsHttps = %d\n", g.urlIsHttps);
printf("g.urlIsSsh = %d\n", g.urlIsSsh);
printf("g.urlProtocol = %s\n", g.urlProtocol);
printf("g.urlName = %s\n", g.urlName);
printf("g.urlPort = %d\n", g.urlPort);
printf("g.urlDfltPort = %d\n", g.urlDfltPort);
printf("g.urlHostname = %s\n", g.urlHostname);
printf("g.urlPath = %s\n", g.urlPath);
printf("g.urlUser = %s\n", g.urlUser);
printf("g.urlPasswd = %s\n", g.urlPasswd);
printf("g.urlCanonical = %s\n", g.urlCanonical);
printf("g.urlFossil = %s\n", g.urlFossil);
if( g.urlIsFile || g.urlIsSsh ) break;
if( i==0 ){
printf("********\n");
url_enable_proxy("Using proxy: ");
}
}
}
|
| ︙ | ︙ | |||
286 287 288 289 290 291 292 |
}
if( zName2 && strcmp(zName2,p->azName[i])==0 ){
zName2 = 0;
z = zValue2;
if( z==0 ) continue;
}
blob_appendf(&p->url, "%s%s=%T", zSep, p->azName[i], z);
| | | 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
}
if( zName2 && strcmp(zName2,p->azName[i])==0 ){
zName2 = 0;
z = zValue2;
if( z==0 ) continue;
}
blob_appendf(&p->url, "%s%s=%T", zSep, p->azName[i], z);
zSep = "&";
}
if( zName1 && zValue1 ){
blob_appendf(&p->url, "%s%s=%T", zSep, zName1, zValue1);
}
if( zName2 && zValue2 ){
blob_appendf(&p->url, "%s%s=%T", zSep, zName2, zValue2);
}
|
| ︙ | ︙ |
Changes to src/user.c.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
break;
}
if( z[i]<' ' ) z[i] = ' ';
}
blob_append(pBlob, z, -1);
}
#ifdef __MINGW32__
/*
** getpass for Windows
*/
static char *getpass(const char *prompt){
static char pwd[64];
size_t i;
| > > > | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
break;
}
if( z[i]<' ' ) z[i] = ' ';
}
blob_append(pBlob, z, -1);
}
#if defined(_WIN32)
#ifdef __MINGW32__
#include <conio.h>
#endif
/*
** getpass for Windows
*/
static char *getpass(const char *prompt){
static char pwd[64];
size_t i;
|
| ︙ | ︙ |
Changes to src/vfile.c.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | ** ** Procedures for managing the VFILE table. */ #include "config.h" #include "vfile.h" #include <assert.h> #include <sys/types.h> #include <dirent.h> /* ** Given a UUID, return the corresponding record ID. If the UUID ** does not exist, then return 0. ** ** For this routine, the UUID must be exact. For a match against ** user input with mixed case, use resolve_uuid(). | > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ** ** Procedures for managing the VFILE table. */ #include "config.h" #include "vfile.h" #include <assert.h> #include <sys/types.h> #if defined(__DMC__) #include "dirent.h" #else #include <dirent.h> #endif /* ** Given a UUID, return the corresponding record ID. If the UUID ** does not exist, then return 0. ** ** For this routine, the UUID must be exact. For a match against ** user input with mixed case, use resolve_uuid(). |
| ︙ | ︙ |
Changes to src/wiki.c.
| ︙ | ︙ | |||
46 47 48 49 50 51 52 |
}
/*
** Output rules for well-formed wiki pages
*/
static void well_formed_wiki_name_rules(void){
@ <ul>
| | | | | | | > > > > > > > > | 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 |
}
/*
** Output rules for well-formed wiki pages
*/
static void well_formed_wiki_name_rules(void){
@ <ul>
@ <li> Must not begin or end with a space.</li>
@ <li> Must not contain any control characters, including tab or
@ newline.</li>
@ <li> Must not have two or more spaces in a row internally.</li>
@ <li> Must be between 3 and 100 characters in length.</li>
@ </ul>
}
/*
** Check a wiki name. If it is not well-formed, then issue an error
** and return true. If it is well-formed, return false.
*/
static int check_name(const char *z){
if( !wiki_name_is_wellformed((const unsigned char *)z) ){
style_header("Wiki Page Name Error");
@ The wiki name "<span class="wikiError">%h(z)</span>" is not well-formed.
@ Rules for wiki page names:
well_formed_wiki_name_rules();
style_footer();
return 1;
}
return 0;
}
/*
** WEBPAGE: home
** WEBPAGE: index
** WEBPAGE: not_found
*/
void home_page(void){
char *zPageName = db_get("project-name",0);
char *zIndexPage = db_get("index-page",0);
login_check_credentials();
if( !g.okRdWiki ){
cgi_redirectf("%s/login?g=%s/home", g.zBaseURL, g.zBaseURL);
}
if( zIndexPage ){
while( zIndexPage[0]=='/' ) zIndexPage++;
if( strcmp(zIndexPage, P("PATH_INFO"))==0 ) zIndexPage = 0;
}
if( zIndexPage ){
cgi_redirectf("%s/%s", g.zBaseURL, zIndexPage);
}
if( zPageName ){
login_check_credentials();
g.zExtra = zPageName;
cgi_set_parameter_nocopy("name", g.zExtra);
g.isHome = 1;
wiki_page();
return;
|
| ︙ | ︙ | |||
145 146 147 148 149 150 151 |
@ <li> Use the <a href="%s(g.zBaseURL)/wiki?name=Sandbox">Sandbox</a>
@ to experiment.</li>
if( g.okNewWiki ){
@ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
}
@ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
@ available on this server.</li>
| | | | | | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
@ <li> Use the <a href="%s(g.zBaseURL)/wiki?name=Sandbox">Sandbox</a>
@ to experiment.</li>
if( g.okNewWiki ){
@ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
}
@ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
@ available on this server.</li>
@ <li> <form method="get" action="%s(g.zBaseURL)/wfind"><div>
@ Search wiki titles: <input type="text" name="title"/>
@ <input type="submit" /></div></form>
@ </li>
@ </ul>
style_footer();
return;
}
if( check_name(zPageName) ) return;
isSandbox = is_sandbox(zPageName);
if( isSandbox ){
|
| ︙ | ︙ | |||
184 185 186 187 188 189 190 |
if( !g.isHome ){
if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
g.zTop, zPageName);
}
if( rid && g.okApndWiki && g.okAttach ){
style_submenu_element("Attach", "Add An Attachment",
| | | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
if( !g.isHome ){
if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
g.zTop, zPageName);
}
if( rid && g.okApndWiki && g.okAttach ){
style_submenu_element("Attach", "Add An Attachment",
"%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
g.zTop, zPageName, g.zTop, zPageName);
}
if( rid && g.okApndWiki ){
style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
g.zTop, zPageName);
}
if( g.okHistory ){
|
| ︙ | ︙ | |||
216 217 218 219 220 221 222 |
const char *zFile = db_column_text(&q, 1);
const char *zUser = db_column_text(&q, 2);
if( cnt==0 ){
@ <hr><h2>Attachments:</h2>
@ <ul>
}
cnt++;
| | > | > | | | | 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 |
const char *zFile = db_column_text(&q, 1);
const char *zUser = db_column_text(&q, 2);
if( cnt==0 ){
@ <hr><h2>Attachments:</h2>
@ <ul>
}
cnt++;
if( g.okHistory && g.okRead ){
@ <li>
@ <a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)">
@ %h(zFile)</a>
}else{
@ <li>%h(zFile)
}
@ added by %h(zUser) on
hyperlink_to_date(zDate, ".");
if( g.okWrWiki && g.okAttach ){
@ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&file=%t(zFile)&from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
}
}
if( cnt ){
@ </ul>
}
db_finalize(&q);
|
| ︙ | ︙ | |||
340 341 342 343 344 345 346 |
zBody = mprintf("<i>Empty Page</i>");
}
zHtmlPageName = mprintf("Edit: %s", zPageName);
style_header(zHtmlPageName);
if( P("preview")!=0 ){
blob_zero(&wiki);
blob_append(&wiki, zBody, -1);
| | | | | | | | | | | 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 |
zBody = mprintf("<i>Empty Page</i>");
}
zHtmlPageName = mprintf("Edit: %s", zPageName);
style_header(zHtmlPageName);
if( P("preview")!=0 ){
blob_zero(&wiki);
blob_append(&wiki, zBody, -1);
@ Preview:<hr />
wiki_convert(&wiki, 0, 0);
@ <hr />
blob_reset(&wiki);
}
for(n=2, z=zBody; z[0]; z++){
if( z[0]=='\n' ) n++;
}
if( n<20 ) n = 20;
if( n>40 ) n = 40;
@ <form method="post" action="%s(g.zBaseURL)/wikiedit"><div>
login_insert_csrf_secret();
@ <input type="hidden" name="name" value="%h(zPageName)" />
@ <textarea name="w" class="wikiedit" cols="80"
@ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
@ <br />
@ <input type="submit" name="preview" value="Preview Your Changes" />
@ <input type="submit" name="submit" value="Apply These Changes" />
@ <input type="submit" name="cancel" value="Cancel" />
@ </div></form>
if( !isSandbox ){
manifest_clear(&m);
}
style_footer();
}
/*
|
| ︙ | ︙ | |||
385 386 387 388 389 390 391 |
return;
}
zName = PD("name","");
if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){
cgi_redirectf("wikiedit?name=%T", zName);
}
style_header("Create A New Wiki Page");
| | < | | | | | | 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 |
return;
}
zName = PD("name","");
if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){
cgi_redirectf("wikiedit?name=%T", zName);
}
style_header("Create A New Wiki Page");
@ <p>Rules for wiki page names:</p>
well_formed_wiki_name_rules();
@ <form method="post" action="%s(g.zBaseURL)/wikinew">
@ <p>Name of new wiki page:
@ <input style="width: 35;" type="text" name="name" value="%h(zName)" />
@ <input type="submit" value="Create" />
@ </p></form>
if( zName[0] ){
@ <p><span class="wikiError">
@ "%h(zName)" is not a valid wiki page name!</span></p>
}
style_footer();
}
/*
** Append the wiki text for an remark to the end of the given BLOB.
|
| ︙ | ︙ | |||
526 527 528 529 530 531 532 |
blob_reset(&preview);
}
zUser = PD("u", g.zLogin);
@ <form method="POST" action="%s(g.zBaseURL)/wikiappend">
login_insert_csrf_secret();
@ <input type="hidden" name="name" value="%h(zPageName)">
@ Your Name:
| | | | | | 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 |
blob_reset(&preview);
}
zUser = PD("u", g.zLogin);
@ <form method="POST" action="%s(g.zBaseURL)/wikiappend">
login_insert_csrf_secret();
@ <input type="hidden" name="name" value="%h(zPageName)">
@ Your Name:
@ <input type="text" name="u" size="20" value="%h(zUser)"><br />
@ Comment to append:<br />
@ <textarea name="r" class="wikiedit" cols="80"
@ rows="10" wrap="virtual">%h(PD("r",""))</textarea>
@ <br />
@ <input type="submit" name="preview" value="Preview Your Comment">
@ <input type="submit" name="submit" value="Append Your Changes">
@ <input type="submit" name="cancel" value="Cancel">
@ </form>
style_footer();
}
/*
** Name of the wiki history page being generated
*/
static const char *zWikiPageName;
/*
** Function called to output extra text at the end of each line in
** a wiki history listing.
*/
static void wiki_history_extra(int rid){
if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
@ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
}
}
/*
** WEBPAGE: whistory
** URL: /whistory?name=PAGENAME
**
|
| ︙ | ︙ | |||
632 633 634 635 636 637 638 |
content_get(rid2, &content2);
manifest_parse(&m2, &content2);
if( m2.type==CFTYPE_WIKI ){
blob_init(&w2, m2.zWiki, -1);
}
}
blob_zero(&d);
| | | 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 |
content_get(rid2, &content2);
manifest_parse(&m2, &content2);
if( m2.type==CFTYPE_WIKI ){
blob_init(&w2, m2.zWiki, -1);
}
}
blob_zero(&d);
text_diff(&w2, &w1, &d, 5, 1);
@ <pre>
@ %h(blob_str(&d))
@ </pre>
style_footer();
}
/*
|
| ︙ | ︙ | |||
729 730 731 732 733 734 735 | @ <li>Most ordinary HTML works.</li> @ <li><verbatim> and <nowiki>.</li> @ </ol> @ <p>We call the first five rules above "wiki" formatting rules. The @ last two rules are the HTML formatting rule.</p> @ <h2>Formatting Rule Details</h2> @ <ol> | | | | | | | | | | | | | 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 |
@ <li>Most ordinary HTML works.</li>
@ <li><verbatim> and <nowiki>.</li>
@ </ol>
@ <p>We call the first five rules above "wiki" formatting rules. The
@ last two rules are the HTML formatting rule.</p>
@ <h2>Formatting Rule Details</h2>
@ <ol>
@ <li> <p><span class="wikiruleHead">Paragraphs</span>. Any sequence of one or more blank lines forms
@ a paragraph break. Centered or right-justified paragraphs are not
@ supported by wiki markup, but you can do these things if you need them
@ using HTML.</p></li>
@ <li> <p><span class="wikiruleHead">Bullet Lists</span>.
@ A bullet list item is a line that begins with a single "*" character
@ surrounded on
@ both sides by two or more spaces or by a tab. Only a single level
@ of bullet list is supported by wiki. For nested lists, use HTML.</p></li>
@ <li> <p><span class="wikiruleHead">Enumeration Lists</span>.
@ An enumeration list item is a line that begins with a single "#" character
@ surrounded on both sides by two or more spaces or by a tab. Only a single
@ level of enumeration list is supported by wiki. For nested lists or for
@ enumerations that count using letters or roman numerials, use HTML.</p></li>
@ <li> <p><span class="wikiruleHead">Indented Paragraphs</span>.
@ Any paragraph that begins with two or more spaces or a tab and
@ which is not a bullet or enumeration list item is rendered
@ indented. Only a single level of indentation is supported by wiki; use
@ HTML for deeper indentation.</p></li>
@ <li> <p><span class="wikiruleHead">Hyperlinks</span>.
@ Text within square brackets ("[...]") becomes a hyperlink. The
@ target can be a wiki page name, the artifact ID of a check-in or ticket,
@ the name of an image, or a URL. By default, the target is displayed
@ as the text of the hyperlink. But you can specify alternative text
@ after the target name separated by a "|" character.</p>
@ <p>You can also link to internal anchor names using [#anchor-name], providing
@ you have added the necessary "<a name="anchor-name"></a>"
@ tag to your wiki page.</p></li>
@ <li> <p><span class="wikiruleHead">HTML</span>.
@ The following standard HTML elements may be used:
@ <a>
@ <address>
@ <b>
@ <big>
@ <blockquote>
@ <br>
|
| ︙ | ︙ | |||
811 812 813 814 815 816 817 | @ <var>. @ In addition, there are two non-standard elements available: @ <verbatim> and <nowiki>. @ No other elements are allowed. All attributes are checked and @ only a few benign attributes are allowed on each element. @ In particular, any attributes that specify javascript or CSS @ are elided.</p></li> | | | | 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 | @ <var>. @ In addition, there are two non-standard elements available: @ <verbatim> and <nowiki>. @ No other elements are allowed. All attributes are checked and @ only a few benign attributes are allowed on each element. @ In particular, any attributes that specify javascript or CSS @ are elided.</p></li> @ <li><p><span class="wikiruleHead">Special Markup.</span> @ The <nowiki> tag disables all wiki formatting rules @ through the matching </nowiki> element. @ The <verbatim> tag works like <pre> with the addition @ that it also disables all wiki and HTML markup @ through the matching </verbatim>.</p></li> @ </ol> style_footer(); } /* ** Add a new wiki page to the respository. The page name is ** given by the zPageName parameter. isNew must be true to create |
| ︙ | ︙ |
Changes to src/wikiformat.c.
| ︙ | ︙ | |||
319 320 321 322 323 324 325 | } return MARKUP_INVALID; } /* ** Token types */ | | | | | | | | | | | | | 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 | } return MARKUP_INVALID; } /* ** Token types */ #define TOKEN_MARKUP 1 /* <...> */ #define TOKEN_CHARACTER 2 /* "&" or "<" not part of markup */ #define TOKEN_LINK 3 /* [...] */ #define TOKEN_PARAGRAPH 4 /* blank lines */ #define TOKEN_NEWLINE 5 /* A single "\n" */ #define TOKEN_BUL_LI 6 /* " * " */ #define TOKEN_NUM_LI 7 /* " # " */ #define TOKEN_ENUM 8 /* " \(?\d+[.)]? " */ #define TOKEN_INDENT 9 /* " " */ #define TOKEN_RAW 10 /* Output exactly (used when wiki-use-html==1) */ #define TOKEN_TEXT 11 /* None of the above */ /* ** State flags */ #define AT_NEWLINE 0x001 /* At start of a line */ #define AT_PARAGRAPH 0x002 /* At start of a paragraph */ #define ALLOW_WIKI 0x004 /* Allow wiki markup */ |
| ︙ | ︙ | |||
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 |
** Render markup on the given blob.
*/
static void renderMarkup(Blob *pOut, ParsedMarkup *p){
int i;
if( p->endTag ){
blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName);
}else{
blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
for(i=0; i<p->nAttr; i++){
blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName);
if( p->aAttr[i].zValue ){
const char *zVal = p->aAttr[i].zValue;
if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/' ){
blob_appendf(pOut, "=\"%s%s\"", g.zBaseURL, zVal);
}else{
blob_appendf(pOut, "=\"%s\"", zVal);
}
}
}
blob_append(pOut, ">", 1);
}
}
/*
** When the markup was parsed, some "\000" may have been inserted.
| > > > > > > | 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 |
** Render markup on the given blob.
*/
static void renderMarkup(Blob *pOut, ParsedMarkup *p){
int i;
if( p->endTag ){
blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName);
}else{
/* Close active paragraph for several elements, which are not allowed
** in paragraphs in XHTML.
*/
blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
for(i=0; i<p->nAttr; i++){
blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName);
if( p->aAttr[i].zValue ){
const char *zVal = p->aAttr[i].zValue;
if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/' ){
blob_appendf(pOut, "=\"%s%s\"", g.zBaseURL, zVal);
}else{
blob_appendf(pOut, "=\"%s\"", zVal);
}
}
}
if (p->iType & MUTYPE_SINGLE){
blob_append(pOut, " /", 2);
}
blob_append(pOut, ">", 1);
}
}
/*
** When the markup was parsed, some "\000" may have been inserted.
|
| ︙ | ︙ | |||
883 884 885 886 887 888 889 |
return p->aStack[i-1].iCode;
}
/*
** Begin a new paragraph if that something that is needed.
*/
static void startAutoParagraph(Renderer *p){
| > | | | 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 |
return p->aStack[i-1].iCode;
}
/*
** Begin a new paragraph if that something that is needed.
*/
static void startAutoParagraph(Renderer *p){
if( p->wantAutoParagraph==0 ) return;
if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
blob_appendf(p->pOut, "<p type=\"auto\">", -1);
pushStack(p, MARKUP_P);
p->wantAutoParagraph = 0;
p->inAutoParagraph = 1;
}
/*
** End a paragraph if we are in one.
|
| ︙ | ︙ | |||
1017 1018 1019 1020 1021 1022 1023 |
int isClosed = 0;
if( is_ticket(zTarget, &isClosed) ){
/* Special display processing for tickets. Display the hyperlink
** as crossed out if the ticket is closed.
*/
if( isClosed ){
if( g.okHistory ){
| | > | | | | | 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 |
int isClosed = 0;
if( is_ticket(zTarget, &isClosed) ){
/* Special display processing for tickets. Display the hyperlink
** as crossed out if the ticket is closed.
*/
if( isClosed ){
if( g.okHistory ){
blob_appendf(p->pOut,
"<a href=\"%s/info/%s\"><span class=\"wikiTagCancelled\">",
g.zBaseURL, zTarget
);
zTerm = "</span></a>";
}else{
blob_appendf(p->pOut,"<span class=\"wikiTagCancelled\">");
zTerm = "</span>";
}
}else{
if( g.okHistory ){
blob_appendf(p->pOut,"<a href=\"%s/info/%s\">",
g.zBaseURL, zTarget
);
}else{
|
| ︙ | ︙ | |||
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 |
if( inlineOnly ){
blob_append(p->pOut, " • ", -1);
}else{
if( p->wikiList!=MARKUP_UL ){
if( p->wikiList ){
popStackToTag(p, p->wikiList);
}
pushStack(p, MARKUP_UL);
blob_append(p->pOut, "<ul>", 4);
p->wikiList = MARKUP_UL;
}
popStackToTag(p, MARKUP_LI);
startAutoParagraph(p);
pushStack(p, MARKUP_LI);
blob_append(p->pOut, "<li>", 4);
}
break;
}
case TOKEN_NUM_LI: {
if( inlineOnly ){
blob_append(p->pOut, " # ", -1);
}else{
if( p->wikiList!=MARKUP_OL ){
if( p->wikiList ){
popStackToTag(p, p->wikiList);
}
pushStack(p, MARKUP_OL);
blob_append(p->pOut, "<ol>", 4);
p->wikiList = MARKUP_OL;
}
popStackToTag(p, MARKUP_LI);
startAutoParagraph(p);
pushStack(p, MARKUP_LI);
blob_append(p->pOut, "<li>", 4);
}
break;
}
case TOKEN_ENUM: {
if( inlineOnly ){
blob_appendf(p->pOut, " (%d) ", atoi(z));
}else{
if( p->wikiList!=MARKUP_OL ){
if( p->wikiList ){
popStackToTag(p, p->wikiList);
}
pushStack(p, MARKUP_OL);
blob_append(p->pOut, "<ol>", 4);
p->wikiList = MARKUP_OL;
}
popStackToTag(p, MARKUP_LI);
startAutoParagraph(p);
pushStack(p, MARKUP_LI);
| > > > | 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 |
if( inlineOnly ){
blob_append(p->pOut, " • ", -1);
}else{
if( p->wikiList!=MARKUP_UL ){
if( p->wikiList ){
popStackToTag(p, p->wikiList);
}
endAutoParagraph(p);
pushStack(p, MARKUP_UL);
blob_append(p->pOut, "<ul>", 4);
p->wikiList = MARKUP_UL;
}
popStackToTag(p, MARKUP_LI);
startAutoParagraph(p);
pushStack(p, MARKUP_LI);
blob_append(p->pOut, "<li>", 4);
}
break;
}
case TOKEN_NUM_LI: {
if( inlineOnly ){
blob_append(p->pOut, " # ", -1);
}else{
if( p->wikiList!=MARKUP_OL ){
if( p->wikiList ){
popStackToTag(p, p->wikiList);
}
endAutoParagraph(p);
pushStack(p, MARKUP_OL);
blob_append(p->pOut, "<ol>", 4);
p->wikiList = MARKUP_OL;
}
popStackToTag(p, MARKUP_LI);
startAutoParagraph(p);
pushStack(p, MARKUP_LI);
blob_append(p->pOut, "<li>", 4);
}
break;
}
case TOKEN_ENUM: {
if( inlineOnly ){
blob_appendf(p->pOut, " (%d) ", atoi(z));
}else{
if( p->wikiList!=MARKUP_OL ){
if( p->wikiList ){
popStackToTag(p, p->wikiList);
}
endAutoParagraph(p);
pushStack(p, MARKUP_OL);
blob_append(p->pOut, "<ol>", 4);
p->wikiList = MARKUP_OL;
}
popStackToTag(p, MARKUP_LI);
startAutoParagraph(p);
pushStack(p, MARKUP_LI);
|
| ︙ | ︙ | |||
1231 1232 1233 1234 1235 1236 1237 |
p->state |= FONT_MARKUP_ONLY;
wiki_render(p, zDisplay);
p->state = savedState;
blob_append(p->pOut, zClose, -1);
break;
}
case TOKEN_TEXT: {
| > > | | 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 |
p->state |= FONT_MARKUP_ONLY;
wiki_render(p, zDisplay);
p->state = savedState;
blob_append(p->pOut, zClose, -1);
break;
}
case TOKEN_TEXT: {
int i;
for(i=0; i<n && isspace(z[i]); i++){}
if( i<n ) startAutoParagraph(p);
blob_append(p->pOut, z, n);
break;
}
case TOKEN_RAW: {
blob_append(p->pOut, z, n);
break;
}
|
| ︙ | ︙ | |||
1345 1346 1347 1348 1349 1350 1351 |
p->zVerbatimId = markup.aAttr[0].zValue;
}else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
blob_appendf(p->pOut, "<pre name='code' class='%s'>",
markup.aAttr[vAttrIdx].zValue);
vAttrDidAppend=1;
}
}
| | > > > | 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 |
p->zVerbatimId = markup.aAttr[0].zValue;
}else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
blob_appendf(p->pOut, "<pre name='code' class='%s'>",
markup.aAttr[vAttrIdx].zValue);
vAttrDidAppend=1;
}
}
if( !vAttrDidAppend ) {
endAutoParagraph(p);
blob_append(p->pOut, "<pre class='verbatim'>",-1);
}
p->wantAutoParagraph = 0;
}else
if( markup.iType==MUTYPE_LI ){
if( backupToType(p, MUTYPE_LIST)==0 ){
endAutoParagraph(p);
pushStack(p, MARKUP_UL);
blob_append(p->pOut, "<ul>", 4);
}
pushStack(p, MARKUP_LI);
renderMarkup(p->pOut, &markup);
}else
if( markup.iType==MUTYPE_TR ){
|
| ︙ | ︙ | |||
1382 1383 1384 1385 1386 1387 1388 |
startAutoParagraph(p);
renderMarkup(p->pOut, &markup);
pushStack(p, markup.iCode);
}else
{
if( markup.iType==MUTYPE_FONT ){
startAutoParagraph(p);
| | > > > > > > > > > > | 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 |
startAutoParagraph(p);
renderMarkup(p->pOut, &markup);
pushStack(p, markup.iCode);
}else
{
if( markup.iType==MUTYPE_FONT ){
startAutoParagraph(p);
}else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
p->wantAutoParagraph = 0;
}
if( markup.iCode==MARKUP_HR
|| markup.iCode==MARKUP_H1
|| markup.iCode==MARKUP_H2
|| markup.iCode==MARKUP_H3
|| markup.iCode==MARKUP_H4
|| markup.iCode==MARKUP_H5
|| markup.iCode==MARKUP_P
){
endAutoParagraph(p);
}
if( (markup.iType & MUTYPE_STACK )!=0 ){
pushStack(p, markup.iCode);
}
renderMarkup(p->pOut, &markup);
}
break;
|
| ︙ | ︙ |
Changes to src/winhttp.c.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file implements a very simple (and low-performance) HTTP server ** for windows. */ | < > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file implements a very simple (and low-performance) HTTP server ** for windows. */ #include "config.h" #ifdef _WIN32 /* This code is for win32 only */ #include "winhttp.h" #include <windows.h> /* ** The HttpRequest structure holds information about each incoming ** HTTP request. */ |
| ︙ | ︙ | |||
131 132 133 134 135 136 137 | ** Start a listening socket and process incoming HTTP requests on ** that socket. */ void win32_http_server( int mnPort, int mxPort, /* Range of allowed TCP port numbers */ const char *zBrowser, /* Command to launch browser. (Or NULL) */ const char *zStopper, /* Stop server when this file is exists (Or NULL) */ | | > | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
** Start a listening socket and process incoming HTTP requests on
** that socket.
*/
void win32_http_server(
int mnPort, int mxPort, /* Range of allowed TCP port numbers */
const char *zBrowser, /* Command to launch browser. (Or NULL) */
const char *zStopper, /* Stop server when this file is exists (Or NULL) */
const char *zNotFound, /* The --notfound option, or NULL */
int flags /* One or more HTTP_SERVER_ flags */
){
WSADATA wd;
SOCKET s = INVALID_SOCKET;
SOCKADDR_IN addr;
int idCnt = 0;
int iPort = mnPort;
char *zNotFoundOption;
|
| ︙ | ︙ | |||
156 157 158 159 160 161 162 |
while( iPort<=mxPort ){
s = socket(AF_INET, SOCK_STREAM, 0);
if( s==INVALID_SOCKET ){
fossil_fatal("unable to create a socket");
}
addr.sin_family = AF_INET;
addr.sin_port = htons(iPort);
| > > > | > | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
while( iPort<=mxPort ){
s = socket(AF_INET, SOCK_STREAM, 0);
if( s==INVALID_SOCKET ){
fossil_fatal("unable to create a socket");
}
addr.sin_family = AF_INET;
addr.sin_port = htons(iPort);
if( flags & HTTP_SERVER_LOCALHOST ){
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
}else{
addr.sin_addr.s_addr = htonl(INADDR_ANY);
}
if( bind(s, (struct sockaddr*)&addr, sizeof(addr))==SOCKET_ERROR ){
closesocket(s);
iPort++;
continue;
}
if( listen(s, SOMAXCONN)==SOCKET_ERROR ){
closesocket(s);
|
| ︙ | ︙ | |||
213 214 215 216 217 218 219 |
p->zNotFound = zNotFoundOption;
_beginthread(win32_process_one_http_request, 0, (void*)p);
}
closesocket(s);
WSACleanup();
}
| | | 219 220 221 222 223 224 225 226 |
p->zNotFound = zNotFoundOption;
_beginthread(win32_process_one_http_request, 0, (void*)p);
}
closesocket(s);
WSACleanup();
}
#endif /* _WIN32 -- This code is for win32 only */
|
Changes to src/xfer.c.
| ︙ | ︙ | |||
984 985 986 987 988 989 990 991 992 993 994 995 996 997 |
nCardSent++;
}
if( pushFlag ){
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
nCardSent++;
}
manifest_crosslink_begin();
fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
while( go ){
int newPhantom = 0;
char *zRandomness;
/* Send make the most recently received cookie. Let the server
| > | 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 |
nCardSent++;
}
if( pushFlag ){
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
nCardSent++;
}
manifest_crosslink_begin();
transport_global_startup();
fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
while( go ){
int newPhantom = 0;
char *zRandomness;
/* Send make the most recently received cookie. Let the server
|
| ︙ | ︙ |
Added win/Makefile.dmc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # DO NOT EDIT # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run # "tclsh src/makemake.tcl dmc > win/Makefile.dmc" # to regenerate this file. B = .. SRCDIR = $B\src OBJDIR = . O = .obj E = .exe # Maybe DMDIR, SSL or INCL needs adjustment DMDIR = c:\DM INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(DMDIR)\extra\include #SSL = -DFOSSIL_ENABLE_SSL=1 SSL = DMCDEF = -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp I18N = -DFOSSIL_I18N=0 CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(I18N) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 SRC = add_.c allrepo_.c attach_.c bag_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c file_.c finfo_.c graph_.c http_.c http_socket_.c http_ssl_.c http_transport_.c info_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c stat_.c style_.c sync_.c tag_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\graph$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\info$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\zip$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O APPNAME = $(OBJDIR)\fossil$(E) all: $(APPNAME) $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\link cd $(OBJDIR) $(DMDIR)\bin\link @link $(OBJDIR)\link: $B\win\Makefile.dmc +echo add allrepo attach bag blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode file finfo graph http http_socket http_ssl http_transport info login main manifest md5 merge merge3 name pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins stat style sync tag th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip sqlite3 th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ translate$E: $(SRCDIR)\translate.c $(BCC) -o$@ $** makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) -o$@ $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) -o$@ $** version$E: $B\win\version.c $(BCC) -o$@ $** $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c $(TCC) -o$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $** $(OBJDIR)\th$O : $(SRCDIR)\th.c $(TCC) -o$@ -c $** $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) -o$@ -c $** VERSION.h : version$E $B\manifest.uuid $B\manifest +$** > $@ page_index.h: mkindex$E $(SRC) +$** > $@ clean: -del $(OBJDIR)\*.obj -del *.obj *_.c *.h *.map realclean: -del $(APPNAME) translate$E mkindex$E makeheaders$E version$E $(OBJDIR)\add$O : add_.c add.h $(TCC) -o$@ -c add_.c add_.c : $(SRCDIR)\add.c +translate$E $** > $@ $(OBJDIR)\allrepo$O : allrepo_.c allrepo.h $(TCC) -o$@ -c allrepo_.c allrepo_.c : $(SRCDIR)\allrepo.c +translate$E $** > $@ $(OBJDIR)\attach$O : attach_.c attach.h $(TCC) -o$@ -c attach_.c attach_.c : $(SRCDIR)\attach.c +translate$E $** > $@ $(OBJDIR)\bag$O : bag_.c bag.h $(TCC) -o$@ -c bag_.c bag_.c : $(SRCDIR)\bag.c +translate$E $** > $@ $(OBJDIR)\blob$O : blob_.c blob.h $(TCC) -o$@ -c blob_.c blob_.c : $(SRCDIR)\blob.c +translate$E $** > $@ $(OBJDIR)\branch$O : branch_.c branch.h $(TCC) -o$@ -c branch_.c branch_.c : $(SRCDIR)\branch.c +translate$E $** > $@ $(OBJDIR)\browse$O : browse_.c browse.h $(TCC) -o$@ -c browse_.c browse_.c : $(SRCDIR)\browse.c +translate$E $** > $@ $(OBJDIR)\captcha$O : captcha_.c captcha.h $(TCC) -o$@ -c captcha_.c captcha_.c : $(SRCDIR)\captcha.c +translate$E $** > $@ $(OBJDIR)\cgi$O : cgi_.c cgi.h $(TCC) -o$@ -c cgi_.c cgi_.c : $(SRCDIR)\cgi.c +translate$E $** > $@ $(OBJDIR)\checkin$O : checkin_.c checkin.h $(TCC) -o$@ -c checkin_.c checkin_.c : $(SRCDIR)\checkin.c +translate$E $** > $@ $(OBJDIR)\checkout$O : checkout_.c checkout.h $(TCC) -o$@ -c checkout_.c checkout_.c : $(SRCDIR)\checkout.c +translate$E $** > $@ $(OBJDIR)\clearsign$O : clearsign_.c clearsign.h $(TCC) -o$@ -c clearsign_.c clearsign_.c : $(SRCDIR)\clearsign.c +translate$E $** > $@ $(OBJDIR)\clone$O : clone_.c clone.h $(TCC) -o$@ -c clone_.c clone_.c : $(SRCDIR)\clone.c +translate$E $** > $@ $(OBJDIR)\comformat$O : comformat_.c comformat.h $(TCC) -o$@ -c comformat_.c comformat_.c : $(SRCDIR)\comformat.c +translate$E $** > $@ $(OBJDIR)\configure$O : configure_.c configure.h $(TCC) -o$@ -c configure_.c configure_.c : $(SRCDIR)\configure.c +translate$E $** > $@ $(OBJDIR)\content$O : content_.c content.h $(TCC) -o$@ -c content_.c content_.c : $(SRCDIR)\content.c +translate$E $** > $@ $(OBJDIR)\db$O : db_.c db.h $(TCC) -o$@ -c db_.c db_.c : $(SRCDIR)\db.c +translate$E $** > $@ $(OBJDIR)\delta$O : delta_.c delta.h $(TCC) -o$@ -c delta_.c delta_.c : $(SRCDIR)\delta.c +translate$E $** > $@ $(OBJDIR)\deltacmd$O : deltacmd_.c deltacmd.h $(TCC) -o$@ -c deltacmd_.c deltacmd_.c : $(SRCDIR)\deltacmd.c +translate$E $** > $@ $(OBJDIR)\descendants$O : descendants_.c descendants.h $(TCC) -o$@ -c descendants_.c descendants_.c : $(SRCDIR)\descendants.c +translate$E $** > $@ $(OBJDIR)\diff$O : diff_.c diff.h $(TCC) -o$@ -c diff_.c diff_.c : $(SRCDIR)\diff.c +translate$E $** > $@ $(OBJDIR)\diffcmd$O : diffcmd_.c diffcmd.h $(TCC) -o$@ -c diffcmd_.c diffcmd_.c : $(SRCDIR)\diffcmd.c +translate$E $** > $@ $(OBJDIR)\doc$O : doc_.c doc.h $(TCC) -o$@ -c doc_.c doc_.c : $(SRCDIR)\doc.c +translate$E $** > $@ $(OBJDIR)\encode$O : encode_.c encode.h $(TCC) -o$@ -c encode_.c encode_.c : $(SRCDIR)\encode.c +translate$E $** > $@ $(OBJDIR)\file$O : file_.c file.h $(TCC) -o$@ -c file_.c file_.c : $(SRCDIR)\file.c +translate$E $** > $@ $(OBJDIR)\finfo$O : finfo_.c finfo.h $(TCC) -o$@ -c finfo_.c finfo_.c : $(SRCDIR)\finfo.c +translate$E $** > $@ $(OBJDIR)\graph$O : graph_.c graph.h $(TCC) -o$@ -c graph_.c graph_.c : $(SRCDIR)\graph.c +translate$E $** > $@ $(OBJDIR)\http$O : http_.c http.h $(TCC) -o$@ -c http_.c http_.c : $(SRCDIR)\http.c +translate$E $** > $@ $(OBJDIR)\http_socket$O : http_socket_.c http_socket.h $(TCC) -o$@ -c http_socket_.c http_socket_.c : $(SRCDIR)\http_socket.c +translate$E $** > $@ $(OBJDIR)\http_ssl$O : http_ssl_.c http_ssl.h $(TCC) -o$@ -c http_ssl_.c http_ssl_.c : $(SRCDIR)\http_ssl.c +translate$E $** > $@ $(OBJDIR)\http_transport$O : http_transport_.c http_transport.h $(TCC) -o$@ -c http_transport_.c http_transport_.c : $(SRCDIR)\http_transport.c +translate$E $** > $@ $(OBJDIR)\info$O : info_.c info.h $(TCC) -o$@ -c info_.c info_.c : $(SRCDIR)\info.c +translate$E $** > $@ $(OBJDIR)\login$O : login_.c login.h $(TCC) -o$@ -c login_.c login_.c : $(SRCDIR)\login.c +translate$E $** > $@ $(OBJDIR)\main$O : main_.c main.h $(TCC) -o$@ -c main_.c main_.c : $(SRCDIR)\main.c +translate$E $** > $@ $(OBJDIR)\manifest$O : manifest_.c manifest.h $(TCC) -o$@ -c manifest_.c manifest_.c : $(SRCDIR)\manifest.c +translate$E $** > $@ $(OBJDIR)\md5$O : md5_.c md5.h $(TCC) -o$@ -c md5_.c md5_.c : $(SRCDIR)\md5.c +translate$E $** > $@ $(OBJDIR)\merge$O : merge_.c merge.h $(TCC) -o$@ -c merge_.c merge_.c : $(SRCDIR)\merge.c +translate$E $** > $@ $(OBJDIR)\merge3$O : merge3_.c merge3.h $(TCC) -o$@ -c merge3_.c merge3_.c : $(SRCDIR)\merge3.c +translate$E $** > $@ $(OBJDIR)\name$O : name_.c name.h $(TCC) -o$@ -c name_.c name_.c : $(SRCDIR)\name.c +translate$E $** > $@ $(OBJDIR)\pivot$O : pivot_.c pivot.h $(TCC) -o$@ -c pivot_.c pivot_.c : $(SRCDIR)\pivot.c +translate$E $** > $@ $(OBJDIR)\popen$O : popen_.c popen.h $(TCC) -o$@ -c popen_.c popen_.c : $(SRCDIR)\popen.c +translate$E $** > $@ $(OBJDIR)\pqueue$O : pqueue_.c pqueue.h $(TCC) -o$@ -c pqueue_.c pqueue_.c : $(SRCDIR)\pqueue.c +translate$E $** > $@ $(OBJDIR)\printf$O : printf_.c printf.h $(TCC) -o$@ -c printf_.c printf_.c : $(SRCDIR)\printf.c +translate$E $** > $@ $(OBJDIR)\rebuild$O : rebuild_.c rebuild.h $(TCC) -o$@ -c rebuild_.c rebuild_.c : $(SRCDIR)\rebuild.c +translate$E $** > $@ $(OBJDIR)\report$O : report_.c report.h $(TCC) -o$@ -c report_.c report_.c : $(SRCDIR)\report.c +translate$E $** > $@ $(OBJDIR)\rss$O : rss_.c rss.h $(TCC) -o$@ -c rss_.c rss_.c : $(SRCDIR)\rss.c +translate$E $** > $@ $(OBJDIR)\schema$O : schema_.c schema.h $(TCC) -o$@ -c schema_.c schema_.c : $(SRCDIR)\schema.c +translate$E $** > $@ $(OBJDIR)\search$O : search_.c search.h $(TCC) -o$@ -c search_.c search_.c : $(SRCDIR)\search.c +translate$E $** > $@ $(OBJDIR)\setup$O : setup_.c setup.h $(TCC) -o$@ -c setup_.c setup_.c : $(SRCDIR)\setup.c +translate$E $** > $@ $(OBJDIR)\sha1$O : sha1_.c sha1.h $(TCC) -o$@ -c sha1_.c sha1_.c : $(SRCDIR)\sha1.c +translate$E $** > $@ $(OBJDIR)\shun$O : shun_.c shun.h $(TCC) -o$@ -c shun_.c shun_.c : $(SRCDIR)\shun.c +translate$E $** > $@ $(OBJDIR)\skins$O : skins_.c skins.h $(TCC) -o$@ -c skins_.c skins_.c : $(SRCDIR)\skins.c +translate$E $** > $@ $(OBJDIR)\stat$O : stat_.c stat.h $(TCC) -o$@ -c stat_.c stat_.c : $(SRCDIR)\stat.c +translate$E $** > $@ $(OBJDIR)\style$O : style_.c style.h $(TCC) -o$@ -c style_.c style_.c : $(SRCDIR)\style.c +translate$E $** > $@ $(OBJDIR)\sync$O : sync_.c sync.h $(TCC) -o$@ -c sync_.c sync_.c : $(SRCDIR)\sync.c +translate$E $** > $@ $(OBJDIR)\tag$O : tag_.c tag.h $(TCC) -o$@ -c tag_.c tag_.c : $(SRCDIR)\tag.c +translate$E $** > $@ $(OBJDIR)\th_main$O : th_main_.c th_main.h $(TCC) -o$@ -c th_main_.c th_main_.c : $(SRCDIR)\th_main.c +translate$E $** > $@ $(OBJDIR)\timeline$O : timeline_.c timeline.h $(TCC) -o$@ -c timeline_.c timeline_.c : $(SRCDIR)\timeline.c +translate$E $** > $@ $(OBJDIR)\tkt$O : tkt_.c tkt.h $(TCC) -o$@ -c tkt_.c tkt_.c : $(SRCDIR)\tkt.c +translate$E $** > $@ $(OBJDIR)\tktsetup$O : tktsetup_.c tktsetup.h $(TCC) -o$@ -c tktsetup_.c tktsetup_.c : $(SRCDIR)\tktsetup.c +translate$E $** > $@ $(OBJDIR)\undo$O : undo_.c undo.h $(TCC) -o$@ -c undo_.c undo_.c : $(SRCDIR)\undo.c +translate$E $** > $@ $(OBJDIR)\update$O : update_.c update.h $(TCC) -o$@ -c update_.c update_.c : $(SRCDIR)\update.c +translate$E $** > $@ $(OBJDIR)\url$O : url_.c url.h $(TCC) -o$@ -c url_.c url_.c : $(SRCDIR)\url.c +translate$E $** > $@ $(OBJDIR)\user$O : user_.c user.h $(TCC) -o$@ -c user_.c user_.c : $(SRCDIR)\user.c +translate$E $** > $@ $(OBJDIR)\verify$O : verify_.c verify.h $(TCC) -o$@ -c verify_.c verify_.c : $(SRCDIR)\verify.c +translate$E $** > $@ $(OBJDIR)\vfile$O : vfile_.c vfile.h $(TCC) -o$@ -c vfile_.c vfile_.c : $(SRCDIR)\vfile.c +translate$E $** > $@ $(OBJDIR)\wiki$O : wiki_.c wiki.h $(TCC) -o$@ -c wiki_.c wiki_.c : $(SRCDIR)\wiki.c +translate$E $** > $@ $(OBJDIR)\wikiformat$O : wikiformat_.c wikiformat.h $(TCC) -o$@ -c wikiformat_.c wikiformat_.c : $(SRCDIR)\wikiformat.c +translate$E $** > $@ $(OBJDIR)\winhttp$O : winhttp_.c winhttp.h $(TCC) -o$@ -c winhttp_.c winhttp_.c : $(SRCDIR)\winhttp.c +translate$E $** > $@ $(OBJDIR)\xfer$O : xfer_.c xfer.h $(TCC) -o$@ -c xfer_.c xfer_.c : $(SRCDIR)\xfer.c +translate$E $** > $@ $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h VERSION.h +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h @copy /Y nul: headers |
Added win/Makefile.msc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # DO NOT EDIT # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run # "tclsh src/makemake.tcl msc > win/Makefile.msc" # to regenerate this file. B = .. SRCDIR = $B\src OBJDIR = . O = .obj E = .exe # Maybe MSCDIR, SSL, ZLIB, or INCL needs adjustment MSCDIR = c:\msc # Uncomment below for SSL support SSL = SSLLIB = #SSL = -DFOSSIL_ENABLE_SSL=1 #SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib advapi32.lib # zlib options # When using precompiled from http://zlib.net/zlib125-dll.zip #ZINCDIR = C:\zlib125-dll\include #ZLIBDIR = C:\zlib125-dll\lib #ZLIB = zdll.lib ZINCDIR = $(MSCDIR)\extra\include ZLIBDIR = $(MSCDIR)\extra\lib ZLIB = zlib.lib INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR) MSCDEF = -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp I18N = -DFOSSIL_I18N=0 CFLAGS = -nologo -MT -O2 BCC = $(CC) $(CFLAGS) TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(I18N) $(SSL) $(INCL) LIBS = $(ZLIB) ws2_32.lib $(SSLLIB) LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR) SRC = add_.c allrepo_.c attach_.c bag_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c file_.c finfo_.c graph_.c http_.c http_socket_.c http_ssl_.c http_transport_.c info_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c stat_.c style_.c sync_.c tag_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\graph$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\info$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\zip$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O APPNAME = $(OBJDIR)\fossil$(E) all: $(OBJDIR) $(APPNAME) $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\linkopts cd $(OBJDIR) link -LINK -OUT:$@ $(LIBDIR) @linkopts $(OBJDIR)\linkopts: $B\win\Makefile.msc echo add allrepo attach bag blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode file finfo graph http http_socket http_ssl http_transport info login main manifest md5 merge merge3 name pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins stat style sync tag th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip sqlite3 th th_lang > $@ echo $(LIBS) >> $@ $(OBJDIR): @-mkdir $@ translate$E: $(SRCDIR)\translate.c $(BCC) $** makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) $** version$E: $B\win\version.c $(BCC) $** $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c $(TCC) /Fo$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $** $(OBJDIR)\th$O : $(SRCDIR)\th.c $(TCC) /Fo$@ -c $** $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) /Fo$@ -c $** VERSION.h : version$E $B\manifest.uuid $B\manifest $** > $@ page_index.h: mkindex$E $(SRC) $** > $@ clean: -del $(OBJDIR)\*.obj -del *.obj *_.c *.h *.map -del headers linkopts realclean: -del $(APPNAME) translate$E mkindex$E makeheaders$E version$E $(OBJDIR)\add$O : add_.c add.h $(TCC) /Fo$@ -c add_.c add_.c : $(SRCDIR)\add.c translate$E $** > $@ $(OBJDIR)\allrepo$O : allrepo_.c allrepo.h $(TCC) /Fo$@ -c allrepo_.c allrepo_.c : $(SRCDIR)\allrepo.c translate$E $** > $@ $(OBJDIR)\attach$O : attach_.c attach.h $(TCC) /Fo$@ -c attach_.c attach_.c : $(SRCDIR)\attach.c translate$E $** > $@ $(OBJDIR)\bag$O : bag_.c bag.h $(TCC) /Fo$@ -c bag_.c bag_.c : $(SRCDIR)\bag.c translate$E $** > $@ $(OBJDIR)\blob$O : blob_.c blob.h $(TCC) /Fo$@ -c blob_.c blob_.c : $(SRCDIR)\blob.c translate$E $** > $@ $(OBJDIR)\branch$O : branch_.c branch.h $(TCC) /Fo$@ -c branch_.c branch_.c : $(SRCDIR)\branch.c translate$E $** > $@ $(OBJDIR)\browse$O : browse_.c browse.h $(TCC) /Fo$@ -c browse_.c browse_.c : $(SRCDIR)\browse.c translate$E $** > $@ $(OBJDIR)\captcha$O : captcha_.c captcha.h $(TCC) /Fo$@ -c captcha_.c captcha_.c : $(SRCDIR)\captcha.c translate$E $** > $@ $(OBJDIR)\cgi$O : cgi_.c cgi.h $(TCC) /Fo$@ -c cgi_.c cgi_.c : $(SRCDIR)\cgi.c translate$E $** > $@ $(OBJDIR)\checkin$O : checkin_.c checkin.h $(TCC) /Fo$@ -c checkin_.c checkin_.c : $(SRCDIR)\checkin.c translate$E $** > $@ $(OBJDIR)\checkout$O : checkout_.c checkout.h $(TCC) /Fo$@ -c checkout_.c checkout_.c : $(SRCDIR)\checkout.c translate$E $** > $@ $(OBJDIR)\clearsign$O : clearsign_.c clearsign.h $(TCC) /Fo$@ -c clearsign_.c clearsign_.c : $(SRCDIR)\clearsign.c translate$E $** > $@ $(OBJDIR)\clone$O : clone_.c clone.h $(TCC) /Fo$@ -c clone_.c clone_.c : $(SRCDIR)\clone.c translate$E $** > $@ $(OBJDIR)\comformat$O : comformat_.c comformat.h $(TCC) /Fo$@ -c comformat_.c comformat_.c : $(SRCDIR)\comformat.c translate$E $** > $@ $(OBJDIR)\configure$O : configure_.c configure.h $(TCC) /Fo$@ -c configure_.c configure_.c : $(SRCDIR)\configure.c translate$E $** > $@ $(OBJDIR)\content$O : content_.c content.h $(TCC) /Fo$@ -c content_.c content_.c : $(SRCDIR)\content.c translate$E $** > $@ $(OBJDIR)\db$O : db_.c db.h $(TCC) /Fo$@ -c db_.c db_.c : $(SRCDIR)\db.c translate$E $** > $@ $(OBJDIR)\delta$O : delta_.c delta.h $(TCC) /Fo$@ -c delta_.c delta_.c : $(SRCDIR)\delta.c translate$E $** > $@ $(OBJDIR)\deltacmd$O : deltacmd_.c deltacmd.h $(TCC) /Fo$@ -c deltacmd_.c deltacmd_.c : $(SRCDIR)\deltacmd.c translate$E $** > $@ $(OBJDIR)\descendants$O : descendants_.c descendants.h $(TCC) /Fo$@ -c descendants_.c descendants_.c : $(SRCDIR)\descendants.c translate$E $** > $@ $(OBJDIR)\diff$O : diff_.c diff.h $(TCC) /Fo$@ -c diff_.c diff_.c : $(SRCDIR)\diff.c translate$E $** > $@ $(OBJDIR)\diffcmd$O : diffcmd_.c diffcmd.h $(TCC) /Fo$@ -c diffcmd_.c diffcmd_.c : $(SRCDIR)\diffcmd.c translate$E $** > $@ $(OBJDIR)\doc$O : doc_.c doc.h $(TCC) /Fo$@ -c doc_.c doc_.c : $(SRCDIR)\doc.c translate$E $** > $@ $(OBJDIR)\encode$O : encode_.c encode.h $(TCC) /Fo$@ -c encode_.c encode_.c : $(SRCDIR)\encode.c translate$E $** > $@ $(OBJDIR)\file$O : file_.c file.h $(TCC) /Fo$@ -c file_.c file_.c : $(SRCDIR)\file.c translate$E $** > $@ $(OBJDIR)\finfo$O : finfo_.c finfo.h $(TCC) /Fo$@ -c finfo_.c finfo_.c : $(SRCDIR)\finfo.c translate$E $** > $@ $(OBJDIR)\graph$O : graph_.c graph.h $(TCC) /Fo$@ -c graph_.c graph_.c : $(SRCDIR)\graph.c translate$E $** > $@ $(OBJDIR)\http$O : http_.c http.h $(TCC) /Fo$@ -c http_.c http_.c : $(SRCDIR)\http.c translate$E $** > $@ $(OBJDIR)\http_socket$O : http_socket_.c http_socket.h $(TCC) /Fo$@ -c http_socket_.c http_socket_.c : $(SRCDIR)\http_socket.c translate$E $** > $@ $(OBJDIR)\http_ssl$O : http_ssl_.c http_ssl.h $(TCC) /Fo$@ -c http_ssl_.c http_ssl_.c : $(SRCDIR)\http_ssl.c translate$E $** > $@ $(OBJDIR)\http_transport$O : http_transport_.c http_transport.h $(TCC) /Fo$@ -c http_transport_.c http_transport_.c : $(SRCDIR)\http_transport.c translate$E $** > $@ $(OBJDIR)\info$O : info_.c info.h $(TCC) /Fo$@ -c info_.c info_.c : $(SRCDIR)\info.c translate$E $** > $@ $(OBJDIR)\login$O : login_.c login.h $(TCC) /Fo$@ -c login_.c login_.c : $(SRCDIR)\login.c translate$E $** > $@ $(OBJDIR)\main$O : main_.c main.h $(TCC) /Fo$@ -c main_.c main_.c : $(SRCDIR)\main.c translate$E $** > $@ $(OBJDIR)\manifest$O : manifest_.c manifest.h $(TCC) /Fo$@ -c manifest_.c manifest_.c : $(SRCDIR)\manifest.c translate$E $** > $@ $(OBJDIR)\md5$O : md5_.c md5.h $(TCC) /Fo$@ -c md5_.c md5_.c : $(SRCDIR)\md5.c translate$E $** > $@ $(OBJDIR)\merge$O : merge_.c merge.h $(TCC) /Fo$@ -c merge_.c merge_.c : $(SRCDIR)\merge.c translate$E $** > $@ $(OBJDIR)\merge3$O : merge3_.c merge3.h $(TCC) /Fo$@ -c merge3_.c merge3_.c : $(SRCDIR)\merge3.c translate$E $** > $@ $(OBJDIR)\name$O : name_.c name.h $(TCC) /Fo$@ -c name_.c name_.c : $(SRCDIR)\name.c translate$E $** > $@ $(OBJDIR)\pivot$O : pivot_.c pivot.h $(TCC) /Fo$@ -c pivot_.c pivot_.c : $(SRCDIR)\pivot.c translate$E $** > $@ $(OBJDIR)\popen$O : popen_.c popen.h $(TCC) /Fo$@ -c popen_.c popen_.c : $(SRCDIR)\popen.c translate$E $** > $@ $(OBJDIR)\pqueue$O : pqueue_.c pqueue.h $(TCC) /Fo$@ -c pqueue_.c pqueue_.c : $(SRCDIR)\pqueue.c translate$E $** > $@ $(OBJDIR)\printf$O : printf_.c printf.h $(TCC) /Fo$@ -c printf_.c printf_.c : $(SRCDIR)\printf.c translate$E $** > $@ $(OBJDIR)\rebuild$O : rebuild_.c rebuild.h $(TCC) /Fo$@ -c rebuild_.c rebuild_.c : $(SRCDIR)\rebuild.c translate$E $** > $@ $(OBJDIR)\report$O : report_.c report.h $(TCC) /Fo$@ -c report_.c report_.c : $(SRCDIR)\report.c translate$E $** > $@ $(OBJDIR)\rss$O : rss_.c rss.h $(TCC) /Fo$@ -c rss_.c rss_.c : $(SRCDIR)\rss.c translate$E $** > $@ $(OBJDIR)\schema$O : schema_.c schema.h $(TCC) /Fo$@ -c schema_.c schema_.c : $(SRCDIR)\schema.c translate$E $** > $@ $(OBJDIR)\search$O : search_.c search.h $(TCC) /Fo$@ -c search_.c search_.c : $(SRCDIR)\search.c translate$E $** > $@ $(OBJDIR)\setup$O : setup_.c setup.h $(TCC) /Fo$@ -c setup_.c setup_.c : $(SRCDIR)\setup.c translate$E $** > $@ $(OBJDIR)\sha1$O : sha1_.c sha1.h $(TCC) /Fo$@ -c sha1_.c sha1_.c : $(SRCDIR)\sha1.c translate$E $** > $@ $(OBJDIR)\shun$O : shun_.c shun.h $(TCC) /Fo$@ -c shun_.c shun_.c : $(SRCDIR)\shun.c translate$E $** > $@ $(OBJDIR)\skins$O : skins_.c skins.h $(TCC) /Fo$@ -c skins_.c skins_.c : $(SRCDIR)\skins.c translate$E $** > $@ $(OBJDIR)\stat$O : stat_.c stat.h $(TCC) /Fo$@ -c stat_.c stat_.c : $(SRCDIR)\stat.c translate$E $** > $@ $(OBJDIR)\style$O : style_.c style.h $(TCC) /Fo$@ -c style_.c style_.c : $(SRCDIR)\style.c translate$E $** > $@ $(OBJDIR)\sync$O : sync_.c sync.h $(TCC) /Fo$@ -c sync_.c sync_.c : $(SRCDIR)\sync.c translate$E $** > $@ $(OBJDIR)\tag$O : tag_.c tag.h $(TCC) /Fo$@ -c tag_.c tag_.c : $(SRCDIR)\tag.c translate$E $** > $@ $(OBJDIR)\th_main$O : th_main_.c th_main.h $(TCC) /Fo$@ -c th_main_.c th_main_.c : $(SRCDIR)\th_main.c translate$E $** > $@ $(OBJDIR)\timeline$O : timeline_.c timeline.h $(TCC) /Fo$@ -c timeline_.c timeline_.c : $(SRCDIR)\timeline.c translate$E $** > $@ $(OBJDIR)\tkt$O : tkt_.c tkt.h $(TCC) /Fo$@ -c tkt_.c tkt_.c : $(SRCDIR)\tkt.c translate$E $** > $@ $(OBJDIR)\tktsetup$O : tktsetup_.c tktsetup.h $(TCC) /Fo$@ -c tktsetup_.c tktsetup_.c : $(SRCDIR)\tktsetup.c translate$E $** > $@ $(OBJDIR)\undo$O : undo_.c undo.h $(TCC) /Fo$@ -c undo_.c undo_.c : $(SRCDIR)\undo.c translate$E $** > $@ $(OBJDIR)\update$O : update_.c update.h $(TCC) /Fo$@ -c update_.c update_.c : $(SRCDIR)\update.c translate$E $** > $@ $(OBJDIR)\url$O : url_.c url.h $(TCC) /Fo$@ -c url_.c url_.c : $(SRCDIR)\url.c translate$E $** > $@ $(OBJDIR)\user$O : user_.c user.h $(TCC) /Fo$@ -c user_.c user_.c : $(SRCDIR)\user.c translate$E $** > $@ $(OBJDIR)\verify$O : verify_.c verify.h $(TCC) /Fo$@ -c verify_.c verify_.c : $(SRCDIR)\verify.c translate$E $** > $@ $(OBJDIR)\vfile$O : vfile_.c vfile.h $(TCC) /Fo$@ -c vfile_.c vfile_.c : $(SRCDIR)\vfile.c translate$E $** > $@ $(OBJDIR)\wiki$O : wiki_.c wiki.h $(TCC) /Fo$@ -c wiki_.c wiki_.c : $(SRCDIR)\wiki.c translate$E $** > $@ $(OBJDIR)\wikiformat$O : wikiformat_.c wikiformat.h $(TCC) /Fo$@ -c wikiformat_.c wikiformat_.c : $(SRCDIR)\wikiformat.c translate$E $** > $@ $(OBJDIR)\winhttp$O : winhttp_.c winhttp.h $(TCC) /Fo$@ -c winhttp_.c winhttp_.c : $(SRCDIR)\winhttp.c translate$E $** > $@ $(OBJDIR)\xfer$O : xfer_.c xfer.h $(TCC) /Fo$@ -c xfer_.c xfer_.c : $(SRCDIR)\xfer.c translate$E $** > $@ $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) /Fo$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c translate$E $** > $@ headers: makeheaders$E page_index.h VERSION.h makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h @copy /Y nul: headers |
Added win/include/dirent.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
/*****************************************************************************
* dirent.h - dirent API for Microsoft Visual Studio
*
* Copyright (C) 2006 Toni Ronkko
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* ``Software''), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Dec 15, 2009, John Cunningham
* Added rewinddir member function
*
* Jan 18, 2008, Toni Ronkko
* Using FindFirstFileA and WIN32_FIND_DATAA to avoid converting string
* between multi-byte and unicode representations. This makes the
* code simpler and also allows the code to be compiled under MingW. Thanks
* to Azriel Fasten for the suggestion.
*
* Mar 4, 2007, Toni Ronkko
* Bug fix: due to the strncpy_s() function this file only compiled in
* Visual Studio 2005. Using the new string functions only when the
* compiler version allows.
*
* Nov 2, 2006, Toni Ronkko
* Major update: removed support for Watcom C, MS-DOS and Turbo C to
* simplify the file, updated the code to compile cleanly on Visual
* Studio 2005 with both unicode and multi-byte character strings,
* removed rewinddir() as it had a bug.
*
* Aug 20, 2006, Toni Ronkko
* Removed all remarks about MSVC 1.0, which is antiqued now. Simplified
* comments by removing SGML tags.
*
* May 14 2002, Toni Ronkko
* Embedded the function definitions directly to the header so that no
* source modules need to be included in the Visual Studio project. Removed
* all the dependencies to other projects so that this very header can be
* used independently.
*
* May 28 1998, Toni Ronkko
* First version.
*****************************************************************************/
#ifndef DIRENT_H
#define DIRENT_H
#include <windows.h>
#include <string.h>
#include <assert.h>
typedef struct dirent
{
char d_name[MAX_PATH + 1]; /* current dir entry (multi-byte char string) */
WIN32_FIND_DATAA data; /* file attributes */
} dirent;
typedef struct DIR
{
dirent current; /* Current directory entry */
int cached; /* Indicates un-processed entry in memory */
HANDLE search_handle; /* File search handle */
char patt[MAX_PATH + 3]; /* search pattern (3 = pattern + "\\*\0") */
} DIR;
/* Forward declarations */
static DIR *opendir (const char *dirname);
static struct dirent *readdir (DIR *dirp);
static int closedir (DIR *dirp);
static void rewinddir(DIR* dirp);
/* Use the new safe string functions introduced in Visual Studio 2005 */
#if defined(_MSC_VER) && _MSC_VER >= 1400
# define STRNCPY(dest,src,size) strncpy_s((dest),(size),(src),_TRUNCATE)
#else
# define STRNCPY(dest,src,size) strncpy((dest),(src),(size))
#endif
/*****************************************************************************
* Open directory stream DIRNAME for read and return a pointer to the
* internal working area that is used to retrieve individual directory
* entries.
*/
static DIR *opendir(const char *dirname)
{
DIR *dirp;
assert (dirname != NULL);
assert (strlen (dirname) < MAX_PATH);
/* construct new DIR structure */
dirp = (DIR*) malloc (sizeof (struct DIR));
if (dirp != NULL) {
char *p;
/* take directory name... */
STRNCPY (dirp->patt, dirname, sizeof(dirp->patt));
dirp->patt[MAX_PATH] = '\0';
/* ... and append search pattern to it */
p = strchr (dirp->patt, '\0');
if (dirp->patt < p && *(p-1) != '\\' && *(p-1) != ':') {
*p++ = '\\';
}
*p++ = '*';
*p = '\0';
/* open stream and retrieve first file */
dirp->search_handle = FindFirstFileA (dirp->patt, &dirp->current.data);
if (dirp->search_handle == INVALID_HANDLE_VALUE) {
/* invalid search pattern? */
free (dirp);
return NULL;
}
/* there is an un-processed directory entry in memory now */
dirp->cached = 1;
}
return dirp;
}
/*****************************************************************************
* Read a directory entry, and return a pointer to a dirent structure
* containing the name of the entry in d_name field. Individual directory
* entries returned by this very function include regular files,
* sub-directories, pseudo-directories "." and "..", but also volume labels,
* hidden files and system files may be returned.
*/
static struct dirent *readdir(DIR *dirp)
{
assert (dirp != NULL);
if (dirp->search_handle == INVALID_HANDLE_VALUE) {
/* directory stream was opened/rewound incorrectly or ended normally */
return NULL;
}
/* get next directory entry */
if (dirp->cached != 0) {
/* a valid directory entry already in memory */
dirp->cached = 0;
} else {
/* read next directory entry from disk */
if (FindNextFileA (dirp->search_handle, &dirp->current.data) == FALSE) {
/* the very last file has been processed or an error occured */
FindClose (dirp->search_handle);
dirp->search_handle = INVALID_HANDLE_VALUE;
return NULL;
}
}
/* copy as a multibyte character string */
STRNCPY ( dirp->current.d_name,
dirp->current.data.cFileName,
sizeof(dirp->current.d_name) );
dirp->current.d_name[MAX_PATH] = '\0';
return &dirp->current;
}
/*****************************************************************************
* Close directory stream opened by opendir() function. Close of the
* directory stream invalidates the DIR structure as well as any previously
* read directory entry.
*/
static int closedir(DIR *dirp)
{
assert (dirp != NULL);
/* release search handle */
if (dirp->search_handle != INVALID_HANDLE_VALUE) {
FindClose (dirp->search_handle);
dirp->search_handle = INVALID_HANDLE_VALUE;
}
/* release directory handle */
free (dirp);
return 0;
}
/*****************************************************************************
* Resets the position of the directory stream to which dirp refers to the
* beginning of the directory. It also causes the directory stream to refer
* to the current state of the corresponding directory, as a call to opendir()
* would have done. If dirp does not refer to a directory stream, the effect
* is undefined.
*/
static void rewinddir(DIR* dirp)
{
/* release search handle */
if (dirp->search_handle != INVALID_HANDLE_VALUE) {
FindClose (dirp->search_handle);
dirp->search_handle = INVALID_HANDLE_VALUE;
}
/* open new search handle and retrieve first file */
dirp->search_handle = FindFirstFileA (dirp->patt, &dirp->current.data);
if (dirp->search_handle == INVALID_HANDLE_VALUE) {
/* invalid search pattern? */
free (dirp);
return;
}
/* there is an un-processed directory entry in memory now */
dirp->cached = 1;
}
#endif /*DIRENT_H*/
|
Added win/include/unistd.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #ifndef _UNISTD_H #define _UNISTD_H 1 /* This file intended to serve as a drop-in replacement for * unistd.h on Windows * Please add functionality as neeeded */ #include <stdlib.h> #include <io.h> #define srandom srand #define random rand #if defined(__DMC__) #endif #if defined(_WIN32) #define _CRT_SECURE_NO_WARNINGS 1 #ifndef F_OK #define F_OK 0 #endif /* not F_OK */ #ifndef X_OK #define X_OK 1 #endif /* not X_OK */ #ifndef R_OK #define R_OK 2 #endif /* not R_OK */ #ifndef W_OK #define W_OK 4 #endif /* not W_OK */ #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif #define access _access #define ftruncate _chsize #define ssize_t int #endif /* unistd.h */ |
Added win/version.c.
> > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <stdio.h>
int main(int argc, char *argv[]){
FILE *m,*u;
char b[10240];
u = fopen(argv[1],"r");
fgets(b, sizeof(b)-1,u);
b[strlen(b)-1] =0;
printf("#define MANIFEST_UUID \"%s\"\n",b);
printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b);
m = fopen(argv[2],"r");
while(b == fgets(b, sizeof(b)-1,m)){
if(0 == strncmp("D ",b,2)){
printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13);
return 0;
}
}
return 1;
}
|
Changes to www/index.wiki.
| ︙ | ︙ | |||
86 87 88 89 90 91 92 |
used by the [./selfhost.wiki | self-hosting fossil repositories].
7. <b>Robust & Reliable</b> -
Fossil stores content using an [./fileformat.wiki | enduring file format]
in an SQLite database so that transactions are
atomic even if interrupted by a power loss or system crash. Furthermore,
automatic [./selfcheck.wiki | self-checks] verify that all aspects of
| | | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
used by the [./selfhost.wiki | self-hosting fossil repositories].
7. <b>Robust & Reliable</b> -
Fossil stores content using an [./fileformat.wiki | enduring file format]
in an SQLite database so that transactions are
atomic even if interrupted by a power loss or system crash. Furthermore,
automatic [./selfcheck.wiki | self-checks] verify that all aspects of
the repository are consistent prior to each commit. In over three years
of operation, no work has ever been lost after having been committed to
a Fossil repository.
<hr>
<h3>Links For Fossil Users:</h3>
* [./reviews.wiki | Testimonials] from satisfied fossil users.
|
| ︙ | ︙ |
Changes to www/password.wiki.
| ︙ | ︙ | |||
58 59 60 61 62 63 64 | all login for that user. Thus, to lock a user out of the system, one has only to set their password to an empty string, using either the web interface or direct SQL manipulation of the USER table. Note also that the password field is essentially ignored for the special users named "anonymous", "developer", "reader", and "nobody". It is not possible to authenticate as users "developer", "reader", or "nobody" and the authentication protocol | | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | all login for that user. Thus, to lock a user out of the system, one has only to set their password to an empty string, using either the web interface or direct SQL manipulation of the USER table. Note also that the password field is essentially ignored for the special users named "anonymous", "developer", "reader", and "nobody". It is not possible to authenticate as users "developer", "reader", or "nobody" and the authentication protocol for "anonymous" uses one-time captchas not persistent passwords. <h2>Web Interface Authentication</h2> When a user logs into Fossil using the web interface, the login name and password are sent in the clear to the server. The server then hashes the password and compares it against the value stored in USER.PW. If they match, the server sets a cookie on the client to record the login. This cookie contains a large amount of high-quality randomness and is thus intractable to guess. The value of the cookie and the IP address of the client is stored in the USER.COOKIE and USER.IPADDR fields of the USER table on the server. The USER.CEXPIRE field holds an expiration date for the cookie, encoded as a julian day number. On all subsequent HTTP requests, the cookie value is matched against the USER table to enable access to the repository. |
| ︙ | ︙ |
Changes to www/quickstart.wiki.
| ︙ | ︙ | |||
230 231 232 233 234 235 236 |
<p>Or</b>
<blockquote>
<b>fossil ui</b> <i>repository-filename</i>
</blockquote>
| | > > | | > | | | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
<p>Or</b>
<blockquote>
<b>fossil ui</b> <i>repository-filename</i>
</blockquote>
<p>The <b>ui</b> command is intended for accessing the web interface
from a local desktop. The <b>ui</b> command binds to the loopback IP
address only (and is thus makes the web interface visible only on the
local machine) and it automatically start your web browser pointing at the
server. For cross-machine collaboration, use the <b>server</b> command,
which binds on all IP addresses and does not try to start a web browser.
You can omit the <i>repository-filename</i> if you are within
a checked-out local tree. The <b>server</b> uses port 8080 by default
but you can specify a different port using the <b>-port</b> option.</p>
<p>Command-line servers like this are useful when two people want
to share a repository on temporary or ad-hoc basis. For a more
permanent installation, you should use either the CGI server or the
inetd server.
<a name="cgiserver"></a>
To use the CGI server, create a CGI script that
|
| ︙ | ︙ |