Fossil SCM
Merge in trunk.
Commit
0ad3e06bd2eff6b3cb6628e9e3cad5976e7ca8653c32baaeb2efb0addeec1d6b
Parent
09239396ae819d2…
26 files changed
+3
-2
+2
+14
+121
-79
+1
-1
+2
-1
+20
+19
-14
+4
-3
+5
-4
+14
-6
+147
-147
+34
-28
+3
-3
+32
-25
+16
-12
+5
-2
+2
-2
+7
-2
+175
-92
+2
-2
+1
-1
+11
+22
-7
+2
-2
+1
-1
~
skins/default/header.txt
~
src/allrepo.c
~
src/branch.c
~
src/browse.c
~
src/capabilities.c
~
src/captcha.c
~
src/content.c
~
src/doc.c
~
src/finfo.c
~
src/forum.c
~
src/http_ssl.c
~
src/info.c
~
src/login.c
~
src/printf.c
~
src/setup.c
~
src/setupuser.c
~
src/shell.c
~
src/shun.c
~
src/skins.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/th_main.c
~
src/timeline.c
~
src/unversioned.c
~
www/cgi.wiki
~
www/index.wiki
+3
-2
| --- skins/default/header.txt | ||
| +++ skins/default/header.txt | ||
| @@ -17,17 +17,18 @@ | ||
| 17 | 17 | html "<a href='$home$url' class='active $cls'>$name</a>\n" |
| 18 | 18 | } else { |
| 19 | 19 | html "<a href='$home$url' class='$cls'>$name</a>\n" |
| 20 | 20 | } |
| 21 | 21 | } |
| 22 | -html "<a id='hbbtn' href='$home/sitemap'>☰</a>" | |
| 22 | +html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" | |
| 23 | 23 | menulink $index_page Home {} |
| 24 | 24 | if {[anycap jor]} { |
| 25 | 25 | menulink /timeline Timeline {} |
| 26 | 26 | } |
| 27 | 27 | if {[hascap oh]} { |
| 28 | - menulink /dir?ci=tip Files desktoponly | |
| 28 | + if {![info exists current_checkin]} {set current_checkin tip} | |
| 29 | + menulink /dir?ci=$current_checkin Files desktoponly | |
| 29 | 30 | } |
| 30 | 31 | if {[hascap o]} { |
| 31 | 32 | menulink /brlist Branches desktoponly |
| 32 | 33 | menulink /taglist Tags wideonly |
| 33 | 34 | } |
| 34 | 35 |
| --- skins/default/header.txt | |
| +++ skins/default/header.txt | |
| @@ -17,17 +17,18 @@ | |
| 17 | html "<a href='$home$url' class='active $cls'>$name</a>\n" |
| 18 | } else { |
| 19 | html "<a href='$home$url' class='$cls'>$name</a>\n" |
| 20 | } |
| 21 | } |
| 22 | html "<a id='hbbtn' href='$home/sitemap'>☰</a>" |
| 23 | menulink $index_page Home {} |
| 24 | if {[anycap jor]} { |
| 25 | menulink /timeline Timeline {} |
| 26 | } |
| 27 | if {[hascap oh]} { |
| 28 | menulink /dir?ci=tip Files desktoponly |
| 29 | } |
| 30 | if {[hascap o]} { |
| 31 | menulink /brlist Branches desktoponly |
| 32 | menulink /taglist Tags wideonly |
| 33 | } |
| 34 |
| --- skins/default/header.txt | |
| +++ skins/default/header.txt | |
| @@ -17,17 +17,18 @@ | |
| 17 | html "<a href='$home$url' class='active $cls'>$name</a>\n" |
| 18 | } else { |
| 19 | html "<a href='$home$url' class='$cls'>$name</a>\n" |
| 20 | } |
| 21 | } |
| 22 | html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" |
| 23 | menulink $index_page Home {} |
| 24 | if {[anycap jor]} { |
| 25 | menulink /timeline Timeline {} |
| 26 | } |
| 27 | if {[hascap oh]} { |
| 28 | if {![info exists current_checkin]} {set current_checkin tip} |
| 29 | menulink /dir?ci=$current_checkin Files desktoponly |
| 30 | } |
| 31 | if {[hascap o]} { |
| 32 | menulink /brlist Branches desktoponly |
| 33 | menulink /taglist Tags wideonly |
| 34 | } |
| 35 |
+2
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -283,11 +283,13 @@ | ||
| 283 | 283 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 284 | 284 | zCmd = "sync -autourl -R"; |
| 285 | 285 | collect_argument(&extra, "verbose","v"); |
| 286 | 286 | collect_argument(&extra, "unversioned","u"); |
| 287 | 287 | }else if( strncmp(zCmd, "test-integrity", n)==0 ){ |
| 288 | + collect_argument(&extra, "db-only", "d"); | |
| 288 | 289 | collect_argument(&extra, "parse", 0); |
| 290 | + collect_argument(&extra, "quick", "q"); | |
| 289 | 291 | zCmd = "test-integrity"; |
| 290 | 292 | }else if( strncmp(zCmd, "test-orphans", n)==0 ){ |
| 291 | 293 | zCmd = "test-orphans -R"; |
| 292 | 294 | }else if( strncmp(zCmd, "test-missing", n)==0 ){ |
| 293 | 295 | zCmd = "test-missing -q -R"; |
| 294 | 296 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -283,11 +283,13 @@ | |
| 283 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 284 | zCmd = "sync -autourl -R"; |
| 285 | collect_argument(&extra, "verbose","v"); |
| 286 | collect_argument(&extra, "unversioned","u"); |
| 287 | }else if( strncmp(zCmd, "test-integrity", n)==0 ){ |
| 288 | collect_argument(&extra, "parse", 0); |
| 289 | zCmd = "test-integrity"; |
| 290 | }else if( strncmp(zCmd, "test-orphans", n)==0 ){ |
| 291 | zCmd = "test-orphans -R"; |
| 292 | }else if( strncmp(zCmd, "test-missing", n)==0 ){ |
| 293 | zCmd = "test-missing -q -R"; |
| 294 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -283,11 +283,13 @@ | |
| 283 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 284 | zCmd = "sync -autourl -R"; |
| 285 | collect_argument(&extra, "verbose","v"); |
| 286 | collect_argument(&extra, "unversioned","u"); |
| 287 | }else if( strncmp(zCmd, "test-integrity", n)==0 ){ |
| 288 | collect_argument(&extra, "db-only", "d"); |
| 289 | collect_argument(&extra, "parse", 0); |
| 290 | collect_argument(&extra, "quick", "q"); |
| 291 | zCmd = "test-integrity"; |
| 292 | }else if( strncmp(zCmd, "test-orphans", n)==0 ){ |
| 293 | zCmd = "test-orphans -R"; |
| 294 | }else if( strncmp(zCmd, "test-missing", n)==0 ){ |
| 295 | zCmd = "test-missing -q -R"; |
| 296 |
+14
| --- src/branch.c | ||
| +++ src/branch.c | ||
| @@ -18,10 +18,24 @@ | ||
| 18 | 18 | ** This file contains code used to create new branches within a repository. |
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "branch.h" |
| 22 | 22 | #include <assert.h> |
| 23 | + | |
| 24 | +/* | |
| 25 | +** Return true if zBr is the branch name associated with check-in with | |
| 26 | +** blob.uuid value of zUuid | |
| 27 | +*/ | |
| 28 | +int branch_includes_uuid(const char *zBr, const char *zUuid){ | |
| 29 | + return db_exists( | |
| 30 | + "SELECT 1 FROM tagxref, blob" | |
| 31 | + " WHERE blob.uuid=%Q AND tagxref.rid=blob.rid" | |
| 32 | + " AND tagxref.value=%Q AND tagxref.tagtype>0" | |
| 33 | + " AND tagxref.tagid=%d", | |
| 34 | + zUuid, zBr, TAG_BRANCH | |
| 35 | + ); | |
| 36 | +} | |
| 23 | 37 | |
| 24 | 38 | /* |
| 25 | 39 | ** If RID refers to a check-in, return the name of the branch for that |
| 26 | 40 | ** check-in. |
| 27 | 41 | ** |
| 28 | 42 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -18,10 +18,24 @@ | |
| 18 | ** This file contains code used to create new branches within a repository. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "branch.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | /* |
| 25 | ** If RID refers to a check-in, return the name of the branch for that |
| 26 | ** check-in. |
| 27 | ** |
| 28 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -18,10 +18,24 @@ | |
| 18 | ** This file contains code used to create new branches within a repository. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "branch.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | /* |
| 25 | ** Return true if zBr is the branch name associated with check-in with |
| 26 | ** blob.uuid value of zUuid |
| 27 | */ |
| 28 | int branch_includes_uuid(const char *zBr, const char *zUuid){ |
| 29 | return db_exists( |
| 30 | "SELECT 1 FROM tagxref, blob" |
| 31 | " WHERE blob.uuid=%Q AND tagxref.rid=blob.rid" |
| 32 | " AND tagxref.value=%Q AND tagxref.tagtype>0" |
| 33 | " AND tagxref.tagid=%d", |
| 34 | zUuid, zBr, TAG_BRANCH |
| 35 | ); |
| 36 | } |
| 37 | |
| 38 | /* |
| 39 | ** If RID refers to a check-in, return the name of the branch for that |
| 40 | ** check-in. |
| 41 | ** |
| 42 |
+121
-79
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -59,10 +59,18 @@ | ||
| 59 | 59 | zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]); |
| 60 | 60 | sqlite3_result_text(context, zOut, i-n+1, sqlite3_free); |
| 61 | 61 | } |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | +/* | |
| 65 | +** Flag arguments for hyperlinked_path() | |
| 66 | +*/ | |
| 67 | +#if INTERFACE | |
| 68 | +# define LINKPATH_FINFO 0x0001 /* Link final term to /finfo */ | |
| 69 | +# define LINKPATH_FILE 0x0002 /* Link final term to /file */ | |
| 70 | +#endif | |
| 71 | + | |
| 64 | 72 | /* |
| 65 | 73 | ** Given a pathname which is a relative path from the root of |
| 66 | 74 | ** the repository to a file or directory, compute a string which |
| 67 | 75 | ** is an HTML rendering of that path with hyperlinks on each |
| 68 | 76 | ** directory component of the path where the hyperlink redirects |
| @@ -76,29 +84,36 @@ | ||
| 76 | 84 | void hyperlinked_path( |
| 77 | 85 | const char *zPath, /* Path to render */ |
| 78 | 86 | Blob *pOut, /* Write into this blob */ |
| 79 | 87 | const char *zCI, /* check-in name, or NULL */ |
| 80 | 88 | const char *zURI, /* "dir" or "tree" */ |
| 81 | - const char *zREx /* Extra query parameters */ | |
| 89 | + const char *zREx, /* Extra query parameters */ | |
| 90 | + unsigned int mFlags /* Extra flags */ | |
| 82 | 91 | ){ |
| 83 | 92 | int i, j; |
| 84 | 93 | char *zSep = ""; |
| 85 | 94 | |
| 86 | 95 | for(i=0; zPath[i]; i=j){ |
| 87 | 96 | for(j=i; zPath[j] && zPath[j]!='/'; j++){} |
| 88 | - if( zPath[j] && g.perm.Hyperlink ){ | |
| 89 | - if( zCI ){ | |
| 90 | - char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI); | |
| 91 | - blob_appendf(pOut, "%s%z%#h</a>", | |
| 92 | - zSep, zLink, j-i, &zPath[i]); | |
| 93 | - }else{ | |
| 94 | - char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx); | |
| 95 | - blob_appendf(pOut, "%s%z%#h</a>", | |
| 96 | - zSep, zLink, j-i, &zPath[i]); | |
| 97 | - } | |
| 98 | - }else{ | |
| 99 | - blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]); | |
| 97 | + if( zPath[j]==0 ){ | |
| 98 | + if( mFlags & LINKPATH_FILE ){ | |
| 99 | + zURI = "file"; | |
| 100 | + }else if( mFlags & LINKPATH_FINFO ){ | |
| 101 | + zURI = "finfo"; | |
| 102 | + }else{ | |
| 103 | + blob_appendf(pOut, "/%h", zPath+i); | |
| 104 | + break; | |
| 105 | + } | |
| 106 | + } | |
| 107 | + if( zCI ){ | |
| 108 | + char *zLink = href("%R/%s?name=%#T%s&ci=%T", zURI, j, zPath, zREx,zCI); | |
| 109 | + blob_appendf(pOut, "%s%z%#h</a>", | |
| 110 | + zSep, zLink, j-i, &zPath[i]); | |
| 111 | + }else{ | |
| 112 | + char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx); | |
| 113 | + blob_appendf(pOut, "%s%z%#h</a>", | |
| 114 | + zSep, zLink, j-i, &zPath[i]); | |
| 100 | 115 | } |
| 101 | 116 | zSep = "/"; |
| 102 | 117 | while( zPath[j]=='/' ){ j++; } |
| 103 | 118 | } |
| 104 | 119 | } |
| @@ -127,17 +142,17 @@ | ||
| 127 | 142 | char *zPrefix; |
| 128 | 143 | Stmt q; |
| 129 | 144 | const char *zCI = P("ci"); |
| 130 | 145 | int rid = 0; |
| 131 | 146 | char *zUuid = 0; |
| 132 | - Blob dirname; | |
| 133 | 147 | Manifest *pM = 0; |
| 134 | 148 | const char *zSubdirLink; |
| 135 | 149 | int linkTrunk = 1; |
| 136 | 150 | int linkTip = 1; |
| 137 | 151 | HQuery sURI; |
| 138 | 152 | int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */ |
| 153 | + int isBranchCI = 0; /* True if ci= refers to a branch name */ | |
| 139 | 154 | char *zHeader = 0; |
| 140 | 155 | |
| 141 | 156 | if( zCI && strlen(zCI)==0 ){ zCI = 0; } |
| 142 | 157 | if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; } |
| 143 | 158 | login_check_credentials(); |
| @@ -157,22 +172,30 @@ | ||
| 157 | 172 | int trunkRid = symbolic_name_to_rid("tag:trunk", "ci"); |
| 158 | 173 | linkTrunk = trunkRid && rid != trunkRid; |
| 159 | 174 | linkTip = rid != symbolic_name_to_rid("tip", "ci"); |
| 160 | 175 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 161 | 176 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0); |
| 177 | + isBranchCI = branch_includes_uuid(zCI, zUuid); | |
| 178 | + Th_Store("current_checkin", zCI); | |
| 162 | 179 | }else{ |
| 163 | 180 | zCI = 0; |
| 164 | 181 | } |
| 165 | 182 | } |
| 166 | 183 | |
| 167 | 184 | assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) ); |
| 168 | - if( isSymbolicCI ) { | |
| 169 | - zHeader = mprintf("%s at %s", (zD ? zD : "Files"), zCI); | |
| 170 | - }else if( zUuid && strlen(zUuid) ){ | |
| 171 | - zHeader = mprintf("%s at [%S]", (zD ? zD : "Files"), zUuid); | |
| 185 | + if( zD==0 ){ | |
| 186 | + if( zCI ){ | |
| 187 | + zHeader = mprintf("Top-level Files of %s", zCI); | |
| 188 | + }else{ | |
| 189 | + zHeader = mprintf("All Top-level Files"); | |
| 190 | + } | |
| 172 | 191 | }else{ |
| 173 | - zHeader = mprintf("%s", (zD ? zD : "All Files")); | |
| 192 | + if( zCI ){ | |
| 193 | + zHeader = mprintf("Files in %s/ of %s", zD, zCI); | |
| 194 | + }else{ | |
| 195 | + zHeader = mprintf("All File in %s/", zD); | |
| 196 | + } | |
| 174 | 197 | } |
| 175 | 198 | style_header("%s", zHeader); |
| 176 | 199 | fossil_free(zHeader); |
| 177 | 200 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 178 | 201 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| @@ -179,47 +202,49 @@ | ||
| 179 | 202 | pathelementFunc, 0, 0); |
| 180 | 203 | url_initialize(&sURI, "dir"); |
| 181 | 204 | cgi_query_parameters_to_url(&sURI); |
| 182 | 205 | |
| 183 | 206 | /* Compute the title of the page */ |
| 184 | - blob_zero(&dirname); | |
| 185 | 207 | if( zD ){ |
| 186 | - blob_append(&dirname, "in directory ", -1); | |
| 187 | - hyperlinked_path(zD, &dirname, zCI, "dir", ""); | |
| 208 | + Blob dirname; | |
| 209 | + blob_init(&dirname, 0, 0); | |
| 210 | + hyperlinked_path(zD, &dirname, zCI, "dir", "", 0); | |
| 211 | + @ <h2>Files in directory %s(blob_str(&dirname)) \ | |
| 212 | + blob_reset(&dirname); | |
| 188 | 213 | zPrefix = mprintf("%s/", zD); |
| 189 | 214 | style_submenu_element("Top-Level", "%s", |
| 190 | 215 | url_render(&sURI, "name", 0, 0, 0)); |
| 191 | 216 | }else{ |
| 192 | - blob_append(&dirname, "in the top-level directory", -1); | |
| 217 | + @ <h2>Files in the top-level directory \ | |
| 193 | 218 | zPrefix = ""; |
| 194 | 219 | } |
| 220 | + if( zCI ){ | |
| 221 | + if( fossil_strcmp(zCI,"tip")==0 ){ | |
| 222 | + @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a></h2> | |
| 223 | + }else if( isBranchCI ){ | |
| 224 | + @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a> \ | |
| 225 | + @ of branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2> | |
| 226 | + }else { | |
| 227 | + @ of check-in %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2> | |
| 228 | + } | |
| 229 | + zSubdirLink = mprintf("%R/dir?ci=%T&name=%T", zCI, zPrefix); | |
| 230 | + if( nD==0 ){ | |
| 231 | + style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); | |
| 232 | + } | |
| 233 | + }else{ | |
| 234 | + @ in any check-in</h2> | |
| 235 | + zSubdirLink = mprintf("%R/dir?name=%T", zPrefix); | |
| 236 | + } | |
| 195 | 237 | if( linkTrunk ){ |
| 196 | 238 | style_submenu_element("Trunk", "%s", |
| 197 | 239 | url_render(&sURI, "ci", "trunk", 0, 0)); |
| 198 | 240 | } |
| 199 | 241 | if( linkTip ){ |
| 200 | 242 | style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0)); |
| 201 | 243 | } |
| 202 | - if( zCI ){ | |
| 203 | - @ <h2>Files at check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] | |
| 204 | - @ %s(blob_str(&dirname)) | |
| 205 | - if( zD ){ | |
| 206 | - @ %z(href("%R/timeline?chng=%T/*", zD))[history]</a> | |
| 207 | - } | |
| 208 | - @ </h2> | |
| 209 | - zSubdirLink = mprintf("%R/dir?ci=%!S&name=%T", zUuid, zPrefix); | |
| 210 | - if( nD==0 ){ | |
| 211 | - style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid); | |
| 212 | - } | |
| 213 | - }else{ | |
| 214 | - @ <h2>All files known in the repository | |
| 215 | - @ %s(blob_str(&dirname)) | |
| 216 | - if( zD ){ | |
| 217 | - @ %z(href("%R/timeline?chng=%T/*", zD))[history]</a> | |
| 218 | - } | |
| 219 | - @ </h2> | |
| 220 | - zSubdirLink = mprintf("%R/dir?name=%T", zPrefix); | |
| 244 | + if( zD ){ | |
| 245 | + style_submenu_element("History","%R/timeline?chng=%T/*", zD); | |
| 221 | 246 | } |
| 222 | 247 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 223 | 248 | style_submenu_element("Tree-View", "%s", |
| 224 | 249 | url_render(&sURI, "type", "tree", 0, 0)); |
| 225 | 250 | |
| @@ -296,11 +321,11 @@ | ||
| 296 | 321 | zFN++; |
| 297 | 322 | @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li> |
| 298 | 323 | }else{ |
| 299 | 324 | const char *zLink; |
| 300 | 325 | if( zCI ){ |
| 301 | - zLink = href("%R/file?name=%T%T&ci=%!S",zPrefix,zFN,zCI); | |
| 326 | + zLink = href("%R/file?name=%T%T&ci=%T",zPrefix,zFN,zCI); | |
| 302 | 327 | }else{ |
| 303 | 328 | zLink = href("%R/finfo?name=%T%T",zPrefix,zFN); |
| 304 | 329 | } |
| 305 | 330 | @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li> |
| 306 | 331 | } |
| @@ -626,10 +651,11 @@ | ||
| 626 | 651 | int startExpanded; /* True to start out with the tree expanded */ |
| 627 | 652 | int showDirOnly; /* Show directories only. Omit files */ |
| 628 | 653 | int nDir = 0; /* Number of directories. Used for ID attributes */ |
| 629 | 654 | char *zProjectName = db_get("project-name", 0); |
| 630 | 655 | int isSymbolicCI = 0; /* ci= is a symbolic name, not a hash prefix */ |
| 656 | + int isBranchCI = 0; /* ci= refers to a branch name */ | |
| 631 | 657 | char *zHeader = 0; |
| 632 | 658 | |
| 633 | 659 | if( zCI && strlen(zCI)==0 ){ zCI = 0; } |
| 634 | 660 | if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; } |
| 635 | 661 | memset(&sTree, 0, sizeof(sTree)); |
| @@ -675,10 +701,12 @@ | ||
| 675 | 701 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 676 | 702 | rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 677 | 703 | zNow = db_text("", "SELECT datetime(mtime,toLocal())" |
| 678 | 704 | " FROM event WHERE objid=%d", rid); |
| 679 | 705 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0); |
| 706 | + isBranchCI = branch_includes_uuid(zCI, zUuid); | |
| 707 | + Th_Store("current_checkin", zCI); | |
| 680 | 708 | }else{ |
| 681 | 709 | zCI = 0; |
| 682 | 710 | } |
| 683 | 711 | } |
| 684 | 712 | if( zCI==0 ){ |
| @@ -685,41 +713,42 @@ | ||
| 685 | 713 | rNow = db_double(0.0, "SELECT max(mtime) FROM event"); |
| 686 | 714 | zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event"); |
| 687 | 715 | } |
| 688 | 716 | |
| 689 | 717 | assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) ); |
| 690 | - if( isSymbolicCI ) { | |
| 691 | - zHeader = mprintf("%s at %s", | |
| 692 | - (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zCI); | |
| 693 | - }else if( zUuid && strlen(zUuid) ){ | |
| 694 | - zHeader = mprintf("%s at [%S]", | |
| 695 | - (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zUuid); | |
| 718 | + if( zD==0 ){ | |
| 719 | + if( zCI ){ | |
| 720 | + zHeader = mprintf("Top-level Files of %s", zCI); | |
| 721 | + }else{ | |
| 722 | + zHeader = mprintf("All Top-level Files"); | |
| 723 | + } | |
| 696 | 724 | }else{ |
| 697 | - zHeader = mprintf("%s", | |
| 698 | - (zD ? zD : (showDirOnly ?"All Folders Hierarchy":"All Files Tree-View"))); | |
| 725 | + if( zCI ){ | |
| 726 | + zHeader = mprintf("Files in %s/ of %s", zD, zCI); | |
| 727 | + }else{ | |
| 728 | + zHeader = mprintf("All File in %s/", zD); | |
| 729 | + } | |
| 699 | 730 | } |
| 700 | 731 | style_header("%s", zHeader); |
| 701 | 732 | fossil_free(zHeader); |
| 702 | 733 | |
| 703 | 734 | /* Compute the title of the page */ |
| 704 | 735 | blob_zero(&dirname); |
| 705 | 736 | if( zD ){ |
| 706 | 737 | blob_append(&dirname, "within directory ", -1); |
| 707 | - hyperlinked_path(zD, &dirname, zCI, "tree", zREx); | |
| 738 | + hyperlinked_path(zD, &dirname, zCI, "tree", zREx, 0); | |
| 708 | 739 | if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE); |
| 709 | 740 | style_submenu_element("Top-Level", "%s", |
| 710 | 741 | url_render(&sURI, "name", 0, 0, 0)); |
| 711 | - }else{ | |
| 712 | - if( zRE ){ | |
| 713 | - blob_appendf(&dirname, "matching \"%s\"", zRE); | |
| 714 | - } | |
| 742 | + }else if( zRE ){ | |
| 743 | + blob_appendf(&dirname, "matching \"%s\"", zRE); | |
| 715 | 744 | } |
| 716 | 745 | style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0); |
| 717 | 746 | if( zCI ){ |
| 718 | 747 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 719 | 748 | if( nD==0 && !showDirOnly ){ |
| 720 | - style_submenu_element("File Ages", "%R/fileage?name=%s", zUuid); | |
| 749 | + style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); | |
| 721 | 750 | } |
| 722 | 751 | } |
| 723 | 752 | if( linkTrunk ){ |
| 724 | 753 | style_submenu_element("Trunk", "%s", |
| 725 | 754 | url_render(&sURI, "ci", "trunk", 0, 0)); |
| @@ -774,10 +803,11 @@ | ||
| 774 | 803 | tree_add_node(&sTree, zName, zUuid, mtime); |
| 775 | 804 | nFile++; |
| 776 | 805 | } |
| 777 | 806 | db_finalize(&q); |
| 778 | 807 | } |
| 808 | + style_submenu_checkbox("nofiles", "Folders Only", 0, 0); | |
| 779 | 809 | |
| 780 | 810 | if( showDirOnly ){ |
| 781 | 811 | for(nFile=0, p=sTree.pFirst; p; p=p->pNext){ |
| 782 | 812 | if( p->pChild!=0 && p->nFullName>nD ) nFile++; |
| 783 | 813 | } |
| @@ -784,18 +814,24 @@ | ||
| 784 | 814 | zObjType = "Folders"; |
| 785 | 815 | }else{ |
| 786 | 816 | zObjType = "Files"; |
| 787 | 817 | } |
| 788 | 818 | |
| 789 | - style_submenu_checkbox("nofiles", "Folders Only", 0, 0); | |
| 790 | - | |
| 791 | - if( zCI ){ | |
| 792 | - @ <h2>%s(zObjType) at check-in | |
| 793 | - if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){ | |
| 794 | - @ "%h(zCI)" | |
| 795 | - } | |
| 796 | - @ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname)) | |
| 819 | + if( zCI && strcmp(zCI,"tip")==0 ){ | |
| 820 | + @ <h2>%s(zObjType) in the %z(href("%R/info?name=tip"))latest check-in</a> | |
| 821 | + }else if( isBranchCI ){ | |
| 822 | + @ <h2>%s(zObjType) in the %z(href("%R/info?name=%T",zCI))latest check-in\ | |
| 823 | + @ </a> for branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a> | |
| 824 | + if( blob_size(&dirname) ){ | |
| 825 | + @ and %s(blob_str(&dirname))</h2> | |
| 826 | + } | |
| 827 | + }else if( zCI ){ | |
| 828 | + @ <h2>%s(zObjType) for check-in \ | |
| 829 | + @ %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2> | |
| 830 | + if( blob_size(&dirname) ){ | |
| 831 | + @ and %s(blob_str(&dirname))</h2> | |
| 832 | + } | |
| 797 | 833 | }else{ |
| 798 | 834 | int n = db_int(0, "SELECT count(*) FROM plink"); |
| 799 | 835 | @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname)) |
| 800 | 836 | } |
| 801 | 837 | if( useMtime ){ |
| @@ -853,11 +889,11 @@ | ||
| 853 | 889 | nDir++; |
| 854 | 890 | }else if( !showDirOnly ){ |
| 855 | 891 | const char *zFileClass = fileext_class(p->zName); |
| 856 | 892 | char *zLink; |
| 857 | 893 | if( zCI ){ |
| 858 | - zLink = href("%R/file?name=%T&ci=%!S",p->zFullName,zCI); | |
| 894 | + zLink = href("%R/file?name=%T&ci=%T",p->zFullName,zCI); | |
| 859 | 895 | }else{ |
| 860 | 896 | zLink = href("%R/finfo?name=%T",p->zFullName); |
| 861 | 897 | } |
| 862 | 898 | @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline"> |
| 863 | 899 | @ %z(zLink)%h(p->zName)</a> |
| @@ -1030,10 +1066,11 @@ | ||
| 1030 | 1066 | int rid; |
| 1031 | 1067 | const char *zName; |
| 1032 | 1068 | const char *zGlob; |
| 1033 | 1069 | const char *zUuid; |
| 1034 | 1070 | const char *zNow; /* Time of check-in */ |
| 1071 | + int isBranchCI; /* name= is a branch name */ | |
| 1035 | 1072 | int showId = PB("showid"); |
| 1036 | 1073 | Stmt q1, q2; |
| 1037 | 1074 | double baseTime; |
| 1038 | 1075 | login_check_credentials(); |
| 1039 | 1076 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| @@ -1043,28 +1080,34 @@ | ||
| 1043 | 1080 | rid = symbolic_name_to_rid(zName, "ci"); |
| 1044 | 1081 | if( rid==0 ){ |
| 1045 | 1082 | fossil_fatal("not a valid check-in: %s", zName); |
| 1046 | 1083 | } |
| 1047 | 1084 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1085 | + isBranchCI = branch_includes_uuid(zName,zUuid); | |
| 1048 | 1086 | baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid); |
| 1049 | 1087 | zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event" |
| 1050 | 1088 | " WHERE objid=%d", rid); |
| 1051 | 1089 | style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName); |
| 1052 | 1090 | style_header("File Ages"); |
| 1053 | 1091 | zGlob = P("glob"); |
| 1054 | 1092 | compute_fileage(rid,zGlob); |
| 1055 | 1093 | db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); |
| 1056 | 1094 | |
| 1057 | - @ <h1>Files in | |
| 1058 | - @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a> | |
| 1095 | + if( fossil_strcmp(zName,"tip")==0 ){ | |
| 1096 | + @ <h1>Files in the %z(href("%R/info?name=tip"))latest check-in</a> | |
| 1097 | + }else if( isBranchCI ){ | |
| 1098 | + @ <h1>Files in the %z(href("%R/info?name=%T",zName))latest check-in</a> | |
| 1099 | + @ of branch %z(href("%R/timeline?r=%T",zName))%h(zName)</a> | |
| 1100 | + }else{ | |
| 1101 | + @ <h1>Files in check-in %z(href("%R/info?name=%T",zName))%h(zName)</a> | |
| 1102 | + } | |
| 1059 | 1103 | if( zGlob && zGlob[0] ){ |
| 1060 | 1104 | @ that match "%h(zGlob)" |
| 1061 | 1105 | } |
| 1062 | 1106 | @ ordered by age</h1> |
| 1063 | 1107 | @ |
| 1064 | - @ <p>File ages are expressed relative to the | |
| 1065 | - @ %z(href("%R/ci/%!S",zUuid))[%S(zUuid)]</a> check-in time of | |
| 1108 | + @ <p>File ages are expressed relative to the check-in time of | |
| 1066 | 1109 | @ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p> |
| 1067 | 1110 | @ |
| 1068 | 1111 | @ <div class='fileage'><table> |
| 1069 | 1112 | @ <tr><th>Age</th><th>Files</th><th>Check-in</th></tr> |
| 1070 | 1113 | db_prepare(&q1, |
| @@ -1079,14 +1122,13 @@ | ||
| 1079 | 1122 | " AND blob.rid=event.objid\n" |
| 1080 | 1123 | " ORDER BY event.mtime DESC;", |
| 1081 | 1124 | TAG_BRANCH |
| 1082 | 1125 | ); |
| 1083 | 1126 | db_prepare(&q2, |
| 1084 | - "SELECT blob.uuid, filename.name, fileage.fid\n" | |
| 1085 | - " FROM fileage, blob, filename\n" | |
| 1127 | + "SELECT filename.name, fileage.fid\n" | |
| 1128 | + " FROM fileage, filename\n" | |
| 1086 | 1129 | " WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid" |
| 1087 | - " AND blob.rid=fileage.fid;" | |
| 1088 | 1130 | ); |
| 1089 | 1131 | while( db_step(&q1)==SQLITE_ROW ){ |
| 1090 | 1132 | double age = baseTime - db_column_double(&q1, 0); |
| 1091 | 1133 | int mid = db_column_int(&q1, 1); |
| 1092 | 1134 | const char *zUuid = db_column_text(&q1, 2); |
| @@ -1096,24 +1138,24 @@ | ||
| 1096 | 1138 | char *zAge = human_readable_age(age); |
| 1097 | 1139 | @ <tr><td>%s(zAge)</td> |
| 1098 | 1140 | @ <td> |
| 1099 | 1141 | db_bind_int(&q2, ":mid", mid); |
| 1100 | 1142 | while( db_step(&q2)==SQLITE_ROW ){ |
| 1101 | - const char *zFUuid = db_column_text(&q2,0); | |
| 1102 | - const char *zFile = db_column_text(&q2,1); | |
| 1103 | - int fid = db_column_int(&q2,2); | |
| 1143 | + const char *zFile = db_column_text(&q2,0); | |
| 1144 | + @ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \ | |
| 1104 | 1145 | if( showId ){ |
| 1105 | - @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a> (%d(fid))<br /> | |
| 1146 | + int fid = db_column_int(&q2,1); | |
| 1147 | + @ (%d(fid))<br /> | |
| 1106 | 1148 | }else{ |
| 1107 | - @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br /> | |
| 1149 | + @ </a><br /> | |
| 1108 | 1150 | } |
| 1109 | 1151 | } |
| 1110 | 1152 | db_reset(&q2); |
| 1111 | 1153 | @ </td> |
| 1112 | 1154 | @ <td> |
| 1113 | 1155 | @ %W(zComment) |
| 1114 | - @ (check-in: %z(href("%R/ci/%!S",zUuid))%S(zUuid)</a>, | |
| 1156 | + @ (check-in: %z(href("%R/info/%!S",zUuid))%S(zUuid)</a>, | |
| 1115 | 1157 | if( showId ){ |
| 1116 | 1158 | @ id: %d(mid) |
| 1117 | 1159 | } |
| 1118 | 1160 | @ user: %z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>, |
| 1119 | 1161 | @ branch: \ |
| 1120 | 1162 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -59,10 +59,18 @@ | |
| 59 | zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]); |
| 60 | sqlite3_result_text(context, zOut, i-n+1, sqlite3_free); |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | /* |
| 65 | ** Given a pathname which is a relative path from the root of |
| 66 | ** the repository to a file or directory, compute a string which |
| 67 | ** is an HTML rendering of that path with hyperlinks on each |
| 68 | ** directory component of the path where the hyperlink redirects |
| @@ -76,29 +84,36 @@ | |
| 76 | void hyperlinked_path( |
| 77 | const char *zPath, /* Path to render */ |
| 78 | Blob *pOut, /* Write into this blob */ |
| 79 | const char *zCI, /* check-in name, or NULL */ |
| 80 | const char *zURI, /* "dir" or "tree" */ |
| 81 | const char *zREx /* Extra query parameters */ |
| 82 | ){ |
| 83 | int i, j; |
| 84 | char *zSep = ""; |
| 85 | |
| 86 | for(i=0; zPath[i]; i=j){ |
| 87 | for(j=i; zPath[j] && zPath[j]!='/'; j++){} |
| 88 | if( zPath[j] && g.perm.Hyperlink ){ |
| 89 | if( zCI ){ |
| 90 | char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI); |
| 91 | blob_appendf(pOut, "%s%z%#h</a>", |
| 92 | zSep, zLink, j-i, &zPath[i]); |
| 93 | }else{ |
| 94 | char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx); |
| 95 | blob_appendf(pOut, "%s%z%#h</a>", |
| 96 | zSep, zLink, j-i, &zPath[i]); |
| 97 | } |
| 98 | }else{ |
| 99 | blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]); |
| 100 | } |
| 101 | zSep = "/"; |
| 102 | while( zPath[j]=='/' ){ j++; } |
| 103 | } |
| 104 | } |
| @@ -127,17 +142,17 @@ | |
| 127 | char *zPrefix; |
| 128 | Stmt q; |
| 129 | const char *zCI = P("ci"); |
| 130 | int rid = 0; |
| 131 | char *zUuid = 0; |
| 132 | Blob dirname; |
| 133 | Manifest *pM = 0; |
| 134 | const char *zSubdirLink; |
| 135 | int linkTrunk = 1; |
| 136 | int linkTip = 1; |
| 137 | HQuery sURI; |
| 138 | int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */ |
| 139 | char *zHeader = 0; |
| 140 | |
| 141 | if( zCI && strlen(zCI)==0 ){ zCI = 0; } |
| 142 | if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; } |
| 143 | login_check_credentials(); |
| @@ -157,22 +172,30 @@ | |
| 157 | int trunkRid = symbolic_name_to_rid("tag:trunk", "ci"); |
| 158 | linkTrunk = trunkRid && rid != trunkRid; |
| 159 | linkTip = rid != symbolic_name_to_rid("tip", "ci"); |
| 160 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 161 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0); |
| 162 | }else{ |
| 163 | zCI = 0; |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) ); |
| 168 | if( isSymbolicCI ) { |
| 169 | zHeader = mprintf("%s at %s", (zD ? zD : "Files"), zCI); |
| 170 | }else if( zUuid && strlen(zUuid) ){ |
| 171 | zHeader = mprintf("%s at [%S]", (zD ? zD : "Files"), zUuid); |
| 172 | }else{ |
| 173 | zHeader = mprintf("%s", (zD ? zD : "All Files")); |
| 174 | } |
| 175 | style_header("%s", zHeader); |
| 176 | fossil_free(zHeader); |
| 177 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 178 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| @@ -179,47 +202,49 @@ | |
| 179 | pathelementFunc, 0, 0); |
| 180 | url_initialize(&sURI, "dir"); |
| 181 | cgi_query_parameters_to_url(&sURI); |
| 182 | |
| 183 | /* Compute the title of the page */ |
| 184 | blob_zero(&dirname); |
| 185 | if( zD ){ |
| 186 | blob_append(&dirname, "in directory ", -1); |
| 187 | hyperlinked_path(zD, &dirname, zCI, "dir", ""); |
| 188 | zPrefix = mprintf("%s/", zD); |
| 189 | style_submenu_element("Top-Level", "%s", |
| 190 | url_render(&sURI, "name", 0, 0, 0)); |
| 191 | }else{ |
| 192 | blob_append(&dirname, "in the top-level directory", -1); |
| 193 | zPrefix = ""; |
| 194 | } |
| 195 | if( linkTrunk ){ |
| 196 | style_submenu_element("Trunk", "%s", |
| 197 | url_render(&sURI, "ci", "trunk", 0, 0)); |
| 198 | } |
| 199 | if( linkTip ){ |
| 200 | style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0)); |
| 201 | } |
| 202 | if( zCI ){ |
| 203 | @ <h2>Files at check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] |
| 204 | @ %s(blob_str(&dirname)) |
| 205 | if( zD ){ |
| 206 | @ %z(href("%R/timeline?chng=%T/*", zD))[history]</a> |
| 207 | } |
| 208 | @ </h2> |
| 209 | zSubdirLink = mprintf("%R/dir?ci=%!S&name=%T", zUuid, zPrefix); |
| 210 | if( nD==0 ){ |
| 211 | style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid); |
| 212 | } |
| 213 | }else{ |
| 214 | @ <h2>All files known in the repository |
| 215 | @ %s(blob_str(&dirname)) |
| 216 | if( zD ){ |
| 217 | @ %z(href("%R/timeline?chng=%T/*", zD))[history]</a> |
| 218 | } |
| 219 | @ </h2> |
| 220 | zSubdirLink = mprintf("%R/dir?name=%T", zPrefix); |
| 221 | } |
| 222 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 223 | style_submenu_element("Tree-View", "%s", |
| 224 | url_render(&sURI, "type", "tree", 0, 0)); |
| 225 | |
| @@ -296,11 +321,11 @@ | |
| 296 | zFN++; |
| 297 | @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li> |
| 298 | }else{ |
| 299 | const char *zLink; |
| 300 | if( zCI ){ |
| 301 | zLink = href("%R/file?name=%T%T&ci=%!S",zPrefix,zFN,zCI); |
| 302 | }else{ |
| 303 | zLink = href("%R/finfo?name=%T%T",zPrefix,zFN); |
| 304 | } |
| 305 | @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li> |
| 306 | } |
| @@ -626,10 +651,11 @@ | |
| 626 | int startExpanded; /* True to start out with the tree expanded */ |
| 627 | int showDirOnly; /* Show directories only. Omit files */ |
| 628 | int nDir = 0; /* Number of directories. Used for ID attributes */ |
| 629 | char *zProjectName = db_get("project-name", 0); |
| 630 | int isSymbolicCI = 0; /* ci= is a symbolic name, not a hash prefix */ |
| 631 | char *zHeader = 0; |
| 632 | |
| 633 | if( zCI && strlen(zCI)==0 ){ zCI = 0; } |
| 634 | if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; } |
| 635 | memset(&sTree, 0, sizeof(sTree)); |
| @@ -675,10 +701,12 @@ | |
| 675 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 676 | rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 677 | zNow = db_text("", "SELECT datetime(mtime,toLocal())" |
| 678 | " FROM event WHERE objid=%d", rid); |
| 679 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0); |
| 680 | }else{ |
| 681 | zCI = 0; |
| 682 | } |
| 683 | } |
| 684 | if( zCI==0 ){ |
| @@ -685,41 +713,42 @@ | |
| 685 | rNow = db_double(0.0, "SELECT max(mtime) FROM event"); |
| 686 | zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event"); |
| 687 | } |
| 688 | |
| 689 | assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) ); |
| 690 | if( isSymbolicCI ) { |
| 691 | zHeader = mprintf("%s at %s", |
| 692 | (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zCI); |
| 693 | }else if( zUuid && strlen(zUuid) ){ |
| 694 | zHeader = mprintf("%s at [%S]", |
| 695 | (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zUuid); |
| 696 | }else{ |
| 697 | zHeader = mprintf("%s", |
| 698 | (zD ? zD : (showDirOnly ?"All Folders Hierarchy":"All Files Tree-View"))); |
| 699 | } |
| 700 | style_header("%s", zHeader); |
| 701 | fossil_free(zHeader); |
| 702 | |
| 703 | /* Compute the title of the page */ |
| 704 | blob_zero(&dirname); |
| 705 | if( zD ){ |
| 706 | blob_append(&dirname, "within directory ", -1); |
| 707 | hyperlinked_path(zD, &dirname, zCI, "tree", zREx); |
| 708 | if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE); |
| 709 | style_submenu_element("Top-Level", "%s", |
| 710 | url_render(&sURI, "name", 0, 0, 0)); |
| 711 | }else{ |
| 712 | if( zRE ){ |
| 713 | blob_appendf(&dirname, "matching \"%s\"", zRE); |
| 714 | } |
| 715 | } |
| 716 | style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0); |
| 717 | if( zCI ){ |
| 718 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 719 | if( nD==0 && !showDirOnly ){ |
| 720 | style_submenu_element("File Ages", "%R/fileage?name=%s", zUuid); |
| 721 | } |
| 722 | } |
| 723 | if( linkTrunk ){ |
| 724 | style_submenu_element("Trunk", "%s", |
| 725 | url_render(&sURI, "ci", "trunk", 0, 0)); |
| @@ -774,10 +803,11 @@ | |
| 774 | tree_add_node(&sTree, zName, zUuid, mtime); |
| 775 | nFile++; |
| 776 | } |
| 777 | db_finalize(&q); |
| 778 | } |
| 779 | |
| 780 | if( showDirOnly ){ |
| 781 | for(nFile=0, p=sTree.pFirst; p; p=p->pNext){ |
| 782 | if( p->pChild!=0 && p->nFullName>nD ) nFile++; |
| 783 | } |
| @@ -784,18 +814,24 @@ | |
| 784 | zObjType = "Folders"; |
| 785 | }else{ |
| 786 | zObjType = "Files"; |
| 787 | } |
| 788 | |
| 789 | style_submenu_checkbox("nofiles", "Folders Only", 0, 0); |
| 790 | |
| 791 | if( zCI ){ |
| 792 | @ <h2>%s(zObjType) at check-in |
| 793 | if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){ |
| 794 | @ "%h(zCI)" |
| 795 | } |
| 796 | @ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname)) |
| 797 | }else{ |
| 798 | int n = db_int(0, "SELECT count(*) FROM plink"); |
| 799 | @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname)) |
| 800 | } |
| 801 | if( useMtime ){ |
| @@ -853,11 +889,11 @@ | |
| 853 | nDir++; |
| 854 | }else if( !showDirOnly ){ |
| 855 | const char *zFileClass = fileext_class(p->zName); |
| 856 | char *zLink; |
| 857 | if( zCI ){ |
| 858 | zLink = href("%R/file?name=%T&ci=%!S",p->zFullName,zCI); |
| 859 | }else{ |
| 860 | zLink = href("%R/finfo?name=%T",p->zFullName); |
| 861 | } |
| 862 | @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline"> |
| 863 | @ %z(zLink)%h(p->zName)</a> |
| @@ -1030,10 +1066,11 @@ | |
| 1030 | int rid; |
| 1031 | const char *zName; |
| 1032 | const char *zGlob; |
| 1033 | const char *zUuid; |
| 1034 | const char *zNow; /* Time of check-in */ |
| 1035 | int showId = PB("showid"); |
| 1036 | Stmt q1, q2; |
| 1037 | double baseTime; |
| 1038 | login_check_credentials(); |
| 1039 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| @@ -1043,28 +1080,34 @@ | |
| 1043 | rid = symbolic_name_to_rid(zName, "ci"); |
| 1044 | if( rid==0 ){ |
| 1045 | fossil_fatal("not a valid check-in: %s", zName); |
| 1046 | } |
| 1047 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1048 | baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid); |
| 1049 | zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event" |
| 1050 | " WHERE objid=%d", rid); |
| 1051 | style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName); |
| 1052 | style_header("File Ages"); |
| 1053 | zGlob = P("glob"); |
| 1054 | compute_fileage(rid,zGlob); |
| 1055 | db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); |
| 1056 | |
| 1057 | @ <h1>Files in |
| 1058 | @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a> |
| 1059 | if( zGlob && zGlob[0] ){ |
| 1060 | @ that match "%h(zGlob)" |
| 1061 | } |
| 1062 | @ ordered by age</h1> |
| 1063 | @ |
| 1064 | @ <p>File ages are expressed relative to the |
| 1065 | @ %z(href("%R/ci/%!S",zUuid))[%S(zUuid)]</a> check-in time of |
| 1066 | @ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p> |
| 1067 | @ |
| 1068 | @ <div class='fileage'><table> |
| 1069 | @ <tr><th>Age</th><th>Files</th><th>Check-in</th></tr> |
| 1070 | db_prepare(&q1, |
| @@ -1079,14 +1122,13 @@ | |
| 1079 | " AND blob.rid=event.objid\n" |
| 1080 | " ORDER BY event.mtime DESC;", |
| 1081 | TAG_BRANCH |
| 1082 | ); |
| 1083 | db_prepare(&q2, |
| 1084 | "SELECT blob.uuid, filename.name, fileage.fid\n" |
| 1085 | " FROM fileage, blob, filename\n" |
| 1086 | " WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid" |
| 1087 | " AND blob.rid=fileage.fid;" |
| 1088 | ); |
| 1089 | while( db_step(&q1)==SQLITE_ROW ){ |
| 1090 | double age = baseTime - db_column_double(&q1, 0); |
| 1091 | int mid = db_column_int(&q1, 1); |
| 1092 | const char *zUuid = db_column_text(&q1, 2); |
| @@ -1096,24 +1138,24 @@ | |
| 1096 | char *zAge = human_readable_age(age); |
| 1097 | @ <tr><td>%s(zAge)</td> |
| 1098 | @ <td> |
| 1099 | db_bind_int(&q2, ":mid", mid); |
| 1100 | while( db_step(&q2)==SQLITE_ROW ){ |
| 1101 | const char *zFUuid = db_column_text(&q2,0); |
| 1102 | const char *zFile = db_column_text(&q2,1); |
| 1103 | int fid = db_column_int(&q2,2); |
| 1104 | if( showId ){ |
| 1105 | @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a> (%d(fid))<br /> |
| 1106 | }else{ |
| 1107 | @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br /> |
| 1108 | } |
| 1109 | } |
| 1110 | db_reset(&q2); |
| 1111 | @ </td> |
| 1112 | @ <td> |
| 1113 | @ %W(zComment) |
| 1114 | @ (check-in: %z(href("%R/ci/%!S",zUuid))%S(zUuid)</a>, |
| 1115 | if( showId ){ |
| 1116 | @ id: %d(mid) |
| 1117 | } |
| 1118 | @ user: %z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>, |
| 1119 | @ branch: \ |
| 1120 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -59,10 +59,18 @@ | |
| 59 | zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]); |
| 60 | sqlite3_result_text(context, zOut, i-n+1, sqlite3_free); |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | /* |
| 65 | ** Flag arguments for hyperlinked_path() |
| 66 | */ |
| 67 | #if INTERFACE |
| 68 | # define LINKPATH_FINFO 0x0001 /* Link final term to /finfo */ |
| 69 | # define LINKPATH_FILE 0x0002 /* Link final term to /file */ |
| 70 | #endif |
| 71 | |
| 72 | /* |
| 73 | ** Given a pathname which is a relative path from the root of |
| 74 | ** the repository to a file or directory, compute a string which |
| 75 | ** is an HTML rendering of that path with hyperlinks on each |
| 76 | ** directory component of the path where the hyperlink redirects |
| @@ -76,29 +84,36 @@ | |
| 84 | void hyperlinked_path( |
| 85 | const char *zPath, /* Path to render */ |
| 86 | Blob *pOut, /* Write into this blob */ |
| 87 | const char *zCI, /* check-in name, or NULL */ |
| 88 | const char *zURI, /* "dir" or "tree" */ |
| 89 | const char *zREx, /* Extra query parameters */ |
| 90 | unsigned int mFlags /* Extra flags */ |
| 91 | ){ |
| 92 | int i, j; |
| 93 | char *zSep = ""; |
| 94 | |
| 95 | for(i=0; zPath[i]; i=j){ |
| 96 | for(j=i; zPath[j] && zPath[j]!='/'; j++){} |
| 97 | if( zPath[j]==0 ){ |
| 98 | if( mFlags & LINKPATH_FILE ){ |
| 99 | zURI = "file"; |
| 100 | }else if( mFlags & LINKPATH_FINFO ){ |
| 101 | zURI = "finfo"; |
| 102 | }else{ |
| 103 | blob_appendf(pOut, "/%h", zPath+i); |
| 104 | break; |
| 105 | } |
| 106 | } |
| 107 | if( zCI ){ |
| 108 | char *zLink = href("%R/%s?name=%#T%s&ci=%T", zURI, j, zPath, zREx,zCI); |
| 109 | blob_appendf(pOut, "%s%z%#h</a>", |
| 110 | zSep, zLink, j-i, &zPath[i]); |
| 111 | }else{ |
| 112 | char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx); |
| 113 | blob_appendf(pOut, "%s%z%#h</a>", |
| 114 | zSep, zLink, j-i, &zPath[i]); |
| 115 | } |
| 116 | zSep = "/"; |
| 117 | while( zPath[j]=='/' ){ j++; } |
| 118 | } |
| 119 | } |
| @@ -127,17 +142,17 @@ | |
| 142 | char *zPrefix; |
| 143 | Stmt q; |
| 144 | const char *zCI = P("ci"); |
| 145 | int rid = 0; |
| 146 | char *zUuid = 0; |
| 147 | Manifest *pM = 0; |
| 148 | const char *zSubdirLink; |
| 149 | int linkTrunk = 1; |
| 150 | int linkTip = 1; |
| 151 | HQuery sURI; |
| 152 | int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */ |
| 153 | int isBranchCI = 0; /* True if ci= refers to a branch name */ |
| 154 | char *zHeader = 0; |
| 155 | |
| 156 | if( zCI && strlen(zCI)==0 ){ zCI = 0; } |
| 157 | if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; } |
| 158 | login_check_credentials(); |
| @@ -157,22 +172,30 @@ | |
| 172 | int trunkRid = symbolic_name_to_rid("tag:trunk", "ci"); |
| 173 | linkTrunk = trunkRid && rid != trunkRid; |
| 174 | linkTip = rid != symbolic_name_to_rid("tip", "ci"); |
| 175 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 176 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0); |
| 177 | isBranchCI = branch_includes_uuid(zCI, zUuid); |
| 178 | Th_Store("current_checkin", zCI); |
| 179 | }else{ |
| 180 | zCI = 0; |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) ); |
| 185 | if( zD==0 ){ |
| 186 | if( zCI ){ |
| 187 | zHeader = mprintf("Top-level Files of %s", zCI); |
| 188 | }else{ |
| 189 | zHeader = mprintf("All Top-level Files"); |
| 190 | } |
| 191 | }else{ |
| 192 | if( zCI ){ |
| 193 | zHeader = mprintf("Files in %s/ of %s", zD, zCI); |
| 194 | }else{ |
| 195 | zHeader = mprintf("All File in %s/", zD); |
| 196 | } |
| 197 | } |
| 198 | style_header("%s", zHeader); |
| 199 | fossil_free(zHeader); |
| 200 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 201 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| @@ -179,47 +202,49 @@ | |
| 202 | pathelementFunc, 0, 0); |
| 203 | url_initialize(&sURI, "dir"); |
| 204 | cgi_query_parameters_to_url(&sURI); |
| 205 | |
| 206 | /* Compute the title of the page */ |
| 207 | if( zD ){ |
| 208 | Blob dirname; |
| 209 | blob_init(&dirname, 0, 0); |
| 210 | hyperlinked_path(zD, &dirname, zCI, "dir", "", 0); |
| 211 | @ <h2>Files in directory %s(blob_str(&dirname)) \ |
| 212 | blob_reset(&dirname); |
| 213 | zPrefix = mprintf("%s/", zD); |
| 214 | style_submenu_element("Top-Level", "%s", |
| 215 | url_render(&sURI, "name", 0, 0, 0)); |
| 216 | }else{ |
| 217 | @ <h2>Files in the top-level directory \ |
| 218 | zPrefix = ""; |
| 219 | } |
| 220 | if( zCI ){ |
| 221 | if( fossil_strcmp(zCI,"tip")==0 ){ |
| 222 | @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a></h2> |
| 223 | }else if( isBranchCI ){ |
| 224 | @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a> \ |
| 225 | @ of branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2> |
| 226 | }else { |
| 227 | @ of check-in %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2> |
| 228 | } |
| 229 | zSubdirLink = mprintf("%R/dir?ci=%T&name=%T", zCI, zPrefix); |
| 230 | if( nD==0 ){ |
| 231 | style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); |
| 232 | } |
| 233 | }else{ |
| 234 | @ in any check-in</h2> |
| 235 | zSubdirLink = mprintf("%R/dir?name=%T", zPrefix); |
| 236 | } |
| 237 | if( linkTrunk ){ |
| 238 | style_submenu_element("Trunk", "%s", |
| 239 | url_render(&sURI, "ci", "trunk", 0, 0)); |
| 240 | } |
| 241 | if( linkTip ){ |
| 242 | style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0)); |
| 243 | } |
| 244 | if( zD ){ |
| 245 | style_submenu_element("History","%R/timeline?chng=%T/*", zD); |
| 246 | } |
| 247 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 248 | style_submenu_element("Tree-View", "%s", |
| 249 | url_render(&sURI, "type", "tree", 0, 0)); |
| 250 | |
| @@ -296,11 +321,11 @@ | |
| 321 | zFN++; |
| 322 | @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li> |
| 323 | }else{ |
| 324 | const char *zLink; |
| 325 | if( zCI ){ |
| 326 | zLink = href("%R/file?name=%T%T&ci=%T",zPrefix,zFN,zCI); |
| 327 | }else{ |
| 328 | zLink = href("%R/finfo?name=%T%T",zPrefix,zFN); |
| 329 | } |
| 330 | @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li> |
| 331 | } |
| @@ -626,10 +651,11 @@ | |
| 651 | int startExpanded; /* True to start out with the tree expanded */ |
| 652 | int showDirOnly; /* Show directories only. Omit files */ |
| 653 | int nDir = 0; /* Number of directories. Used for ID attributes */ |
| 654 | char *zProjectName = db_get("project-name", 0); |
| 655 | int isSymbolicCI = 0; /* ci= is a symbolic name, not a hash prefix */ |
| 656 | int isBranchCI = 0; /* ci= refers to a branch name */ |
| 657 | char *zHeader = 0; |
| 658 | |
| 659 | if( zCI && strlen(zCI)==0 ){ zCI = 0; } |
| 660 | if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; } |
| 661 | memset(&sTree, 0, sizeof(sTree)); |
| @@ -675,10 +701,12 @@ | |
| 701 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 702 | rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 703 | zNow = db_text("", "SELECT datetime(mtime,toLocal())" |
| 704 | " FROM event WHERE objid=%d", rid); |
| 705 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0); |
| 706 | isBranchCI = branch_includes_uuid(zCI, zUuid); |
| 707 | Th_Store("current_checkin", zCI); |
| 708 | }else{ |
| 709 | zCI = 0; |
| 710 | } |
| 711 | } |
| 712 | if( zCI==0 ){ |
| @@ -685,41 +713,42 @@ | |
| 713 | rNow = db_double(0.0, "SELECT max(mtime) FROM event"); |
| 714 | zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event"); |
| 715 | } |
| 716 | |
| 717 | assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) ); |
| 718 | if( zD==0 ){ |
| 719 | if( zCI ){ |
| 720 | zHeader = mprintf("Top-level Files of %s", zCI); |
| 721 | }else{ |
| 722 | zHeader = mprintf("All Top-level Files"); |
| 723 | } |
| 724 | }else{ |
| 725 | if( zCI ){ |
| 726 | zHeader = mprintf("Files in %s/ of %s", zD, zCI); |
| 727 | }else{ |
| 728 | zHeader = mprintf("All File in %s/", zD); |
| 729 | } |
| 730 | } |
| 731 | style_header("%s", zHeader); |
| 732 | fossil_free(zHeader); |
| 733 | |
| 734 | /* Compute the title of the page */ |
| 735 | blob_zero(&dirname); |
| 736 | if( zD ){ |
| 737 | blob_append(&dirname, "within directory ", -1); |
| 738 | hyperlinked_path(zD, &dirname, zCI, "tree", zREx, 0); |
| 739 | if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE); |
| 740 | style_submenu_element("Top-Level", "%s", |
| 741 | url_render(&sURI, "name", 0, 0, 0)); |
| 742 | }else if( zRE ){ |
| 743 | blob_appendf(&dirname, "matching \"%s\"", zRE); |
| 744 | } |
| 745 | style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0); |
| 746 | if( zCI ){ |
| 747 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 748 | if( nD==0 && !showDirOnly ){ |
| 749 | style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); |
| 750 | } |
| 751 | } |
| 752 | if( linkTrunk ){ |
| 753 | style_submenu_element("Trunk", "%s", |
| 754 | url_render(&sURI, "ci", "trunk", 0, 0)); |
| @@ -774,10 +803,11 @@ | |
| 803 | tree_add_node(&sTree, zName, zUuid, mtime); |
| 804 | nFile++; |
| 805 | } |
| 806 | db_finalize(&q); |
| 807 | } |
| 808 | style_submenu_checkbox("nofiles", "Folders Only", 0, 0); |
| 809 | |
| 810 | if( showDirOnly ){ |
| 811 | for(nFile=0, p=sTree.pFirst; p; p=p->pNext){ |
| 812 | if( p->pChild!=0 && p->nFullName>nD ) nFile++; |
| 813 | } |
| @@ -784,18 +814,24 @@ | |
| 814 | zObjType = "Folders"; |
| 815 | }else{ |
| 816 | zObjType = "Files"; |
| 817 | } |
| 818 | |
| 819 | if( zCI && strcmp(zCI,"tip")==0 ){ |
| 820 | @ <h2>%s(zObjType) in the %z(href("%R/info?name=tip"))latest check-in</a> |
| 821 | }else if( isBranchCI ){ |
| 822 | @ <h2>%s(zObjType) in the %z(href("%R/info?name=%T",zCI))latest check-in\ |
| 823 | @ </a> for branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a> |
| 824 | if( blob_size(&dirname) ){ |
| 825 | @ and %s(blob_str(&dirname))</h2> |
| 826 | } |
| 827 | }else if( zCI ){ |
| 828 | @ <h2>%s(zObjType) for check-in \ |
| 829 | @ %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2> |
| 830 | if( blob_size(&dirname) ){ |
| 831 | @ and %s(blob_str(&dirname))</h2> |
| 832 | } |
| 833 | }else{ |
| 834 | int n = db_int(0, "SELECT count(*) FROM plink"); |
| 835 | @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname)) |
| 836 | } |
| 837 | if( useMtime ){ |
| @@ -853,11 +889,11 @@ | |
| 889 | nDir++; |
| 890 | }else if( !showDirOnly ){ |
| 891 | const char *zFileClass = fileext_class(p->zName); |
| 892 | char *zLink; |
| 893 | if( zCI ){ |
| 894 | zLink = href("%R/file?name=%T&ci=%T",p->zFullName,zCI); |
| 895 | }else{ |
| 896 | zLink = href("%R/finfo?name=%T",p->zFullName); |
| 897 | } |
| 898 | @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline"> |
| 899 | @ %z(zLink)%h(p->zName)</a> |
| @@ -1030,10 +1066,11 @@ | |
| 1066 | int rid; |
| 1067 | const char *zName; |
| 1068 | const char *zGlob; |
| 1069 | const char *zUuid; |
| 1070 | const char *zNow; /* Time of check-in */ |
| 1071 | int isBranchCI; /* name= is a branch name */ |
| 1072 | int showId = PB("showid"); |
| 1073 | Stmt q1, q2; |
| 1074 | double baseTime; |
| 1075 | login_check_credentials(); |
| 1076 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| @@ -1043,28 +1080,34 @@ | |
| 1080 | rid = symbolic_name_to_rid(zName, "ci"); |
| 1081 | if( rid==0 ){ |
| 1082 | fossil_fatal("not a valid check-in: %s", zName); |
| 1083 | } |
| 1084 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1085 | isBranchCI = branch_includes_uuid(zName,zUuid); |
| 1086 | baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid); |
| 1087 | zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event" |
| 1088 | " WHERE objid=%d", rid); |
| 1089 | style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName); |
| 1090 | style_header("File Ages"); |
| 1091 | zGlob = P("glob"); |
| 1092 | compute_fileage(rid,zGlob); |
| 1093 | db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); |
| 1094 | |
| 1095 | if( fossil_strcmp(zName,"tip")==0 ){ |
| 1096 | @ <h1>Files in the %z(href("%R/info?name=tip"))latest check-in</a> |
| 1097 | }else if( isBranchCI ){ |
| 1098 | @ <h1>Files in the %z(href("%R/info?name=%T",zName))latest check-in</a> |
| 1099 | @ of branch %z(href("%R/timeline?r=%T",zName))%h(zName)</a> |
| 1100 | }else{ |
| 1101 | @ <h1>Files in check-in %z(href("%R/info?name=%T",zName))%h(zName)</a> |
| 1102 | } |
| 1103 | if( zGlob && zGlob[0] ){ |
| 1104 | @ that match "%h(zGlob)" |
| 1105 | } |
| 1106 | @ ordered by age</h1> |
| 1107 | @ |
| 1108 | @ <p>File ages are expressed relative to the check-in time of |
| 1109 | @ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p> |
| 1110 | @ |
| 1111 | @ <div class='fileage'><table> |
| 1112 | @ <tr><th>Age</th><th>Files</th><th>Check-in</th></tr> |
| 1113 | db_prepare(&q1, |
| @@ -1079,14 +1122,13 @@ | |
| 1122 | " AND blob.rid=event.objid\n" |
| 1123 | " ORDER BY event.mtime DESC;", |
| 1124 | TAG_BRANCH |
| 1125 | ); |
| 1126 | db_prepare(&q2, |
| 1127 | "SELECT filename.name, fileage.fid\n" |
| 1128 | " FROM fileage, filename\n" |
| 1129 | " WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid" |
| 1130 | ); |
| 1131 | while( db_step(&q1)==SQLITE_ROW ){ |
| 1132 | double age = baseTime - db_column_double(&q1, 0); |
| 1133 | int mid = db_column_int(&q1, 1); |
| 1134 | const char *zUuid = db_column_text(&q1, 2); |
| @@ -1096,24 +1138,24 @@ | |
| 1138 | char *zAge = human_readable_age(age); |
| 1139 | @ <tr><td>%s(zAge)</td> |
| 1140 | @ <td> |
| 1141 | db_bind_int(&q2, ":mid", mid); |
| 1142 | while( db_step(&q2)==SQLITE_ROW ){ |
| 1143 | const char *zFile = db_column_text(&q2,0); |
| 1144 | @ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \ |
| 1145 | if( showId ){ |
| 1146 | int fid = db_column_int(&q2,1); |
| 1147 | @ (%d(fid))<br /> |
| 1148 | }else{ |
| 1149 | @ </a><br /> |
| 1150 | } |
| 1151 | } |
| 1152 | db_reset(&q2); |
| 1153 | @ </td> |
| 1154 | @ <td> |
| 1155 | @ %W(zComment) |
| 1156 | @ (check-in: %z(href("%R/info/%!S",zUuid))%S(zUuid)</a>, |
| 1157 | if( showId ){ |
| 1158 | @ id: %d(mid) |
| 1159 | } |
| 1160 | @ user: %z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>, |
| 1161 | @ branch: \ |
| 1162 |
+1
-1
| --- src/capabilities.c | ||
| +++ src/capabilities.c | ||
| @@ -239,11 +239,11 @@ | ||
| 239 | 239 | char *zOneLiner; /* One-line summary */ |
| 240 | 240 | } aCap[] = { |
| 241 | 241 | { 'a', CAPCLASS_SUPER, 0, |
| 242 | 242 | "Admin", "Create and delete users" }, |
| 243 | 243 | { 'b', CAPCLASS_WIKI|CAPCLASS_TKT, 0, |
| 244 | - "Attach", "Add attchments to wiki or tickets" }, | |
| 244 | + "Attach", "Add attachments to wiki or tickets" }, | |
| 245 | 245 | { 'c', CAPCLASS_TKT, 0, |
| 246 | 246 | "Append-Tkt", "Append to existing tickets" }, |
| 247 | 247 | /* |
| 248 | 248 | ** d unused since fork from CVSTrac; |
| 249 | 249 | ** see https://fossil-scm.org/forum/forumpost/43c78f4bef |
| 250 | 250 |
| --- src/capabilities.c | |
| +++ src/capabilities.c | |
| @@ -239,11 +239,11 @@ | |
| 239 | char *zOneLiner; /* One-line summary */ |
| 240 | } aCap[] = { |
| 241 | { 'a', CAPCLASS_SUPER, 0, |
| 242 | "Admin", "Create and delete users" }, |
| 243 | { 'b', CAPCLASS_WIKI|CAPCLASS_TKT, 0, |
| 244 | "Attach", "Add attchments to wiki or tickets" }, |
| 245 | { 'c', CAPCLASS_TKT, 0, |
| 246 | "Append-Tkt", "Append to existing tickets" }, |
| 247 | /* |
| 248 | ** d unused since fork from CVSTrac; |
| 249 | ** see https://fossil-scm.org/forum/forumpost/43c78f4bef |
| 250 |
| --- src/capabilities.c | |
| +++ src/capabilities.c | |
| @@ -239,11 +239,11 @@ | |
| 239 | char *zOneLiner; /* One-line summary */ |
| 240 | } aCap[] = { |
| 241 | { 'a', CAPCLASS_SUPER, 0, |
| 242 | "Admin", "Create and delete users" }, |
| 243 | { 'b', CAPCLASS_WIKI|CAPCLASS_TKT, 0, |
| 244 | "Attach", "Add attachments to wiki or tickets" }, |
| 245 | { 'c', CAPCLASS_TKT, 0, |
| 246 | "Append-Tkt", "Append to existing tickets" }, |
| 247 | /* |
| 248 | ** d unused since fork from CVSTrac; |
| 249 | ** see https://fossil-scm.org/forum/forumpost/43c78f4bef |
| 250 |
+2
-1
| --- src/captcha.c | ||
| +++ src/captcha.c | ||
| @@ -558,11 +558,12 @@ | ||
| 558 | 558 | /* |
| 559 | 559 | ** Add a "Speak the captcha" button. |
| 560 | 560 | */ |
| 561 | 561 | void captcha_speakit_button(unsigned int uSeed, const char *zMsg){ |
| 562 | 562 | if( zMsg==0 ) zMsg = "Speak the text"; |
| 563 | - @ <input type="button" value="%h(zMsg)" id="speakthetext"> | |
| 563 | + @ <input aria-label="%h(zMsg)" type="button" value="%h(zMsg)" \ | |
| 564 | + @ id="speakthetext"> | |
| 564 | 565 | @ <script nonce="%h(style_nonce())"> |
| 565 | 566 | @ document.getElementById("speakthetext").onclick = function(){ |
| 566 | 567 | @ var audio = window.fossilAudioCaptcha \ |
| 567 | 568 | @ || new Audio("%R/captcha-audio/%u(uSeed)"); |
| 568 | 569 | @ window.fossilAudioCaptcha = audio; |
| 569 | 570 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -558,11 +558,12 @@ | |
| 558 | /* |
| 559 | ** Add a "Speak the captcha" button. |
| 560 | */ |
| 561 | void captcha_speakit_button(unsigned int uSeed, const char *zMsg){ |
| 562 | if( zMsg==0 ) zMsg = "Speak the text"; |
| 563 | @ <input type="button" value="%h(zMsg)" id="speakthetext"> |
| 564 | @ <script nonce="%h(style_nonce())"> |
| 565 | @ document.getElementById("speakthetext").onclick = function(){ |
| 566 | @ var audio = window.fossilAudioCaptcha \ |
| 567 | @ || new Audio("%R/captcha-audio/%u(uSeed)"); |
| 568 | @ window.fossilAudioCaptcha = audio; |
| 569 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -558,11 +558,12 @@ | |
| 558 | /* |
| 559 | ** Add a "Speak the captcha" button. |
| 560 | */ |
| 561 | void captcha_speakit_button(unsigned int uSeed, const char *zMsg){ |
| 562 | if( zMsg==0 ) zMsg = "Speak the text"; |
| 563 | @ <input aria-label="%h(zMsg)" type="button" value="%h(zMsg)" \ |
| 564 | @ id="speakthetext"> |
| 565 | @ <script nonce="%h(style_nonce())"> |
| 566 | @ document.getElementById("speakthetext").onclick = function(){ |
| 567 | @ var audio = window.fossilAudioCaptcha \ |
| 568 | @ || new Audio("%R/captcha-audio/%u(uSeed)"); |
| 569 | @ window.fossilAudioCaptcha = audio; |
| 570 |
+20
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -944,13 +944,19 @@ | ||
| 944 | 944 | ** Verify that all content can be extracted from the BLOB table correctly. |
| 945 | 945 | ** If the BLOB table is correct, then the repository can always be |
| 946 | 946 | ** successfully reconstructed using "fossil rebuild". |
| 947 | 947 | ** |
| 948 | 948 | ** Options: |
| 949 | +** | |
| 950 | +** -d|--db-only Run "PRAGMA integrity_check" on the database only. | |
| 951 | +** No other validation is performed. | |
| 949 | 952 | ** |
| 950 | 953 | ** --parse Parse all manifests, wikis, tickets, events, and |
| 951 | 954 | ** so forth, reporting any errors found. |
| 955 | +** | |
| 956 | +** -q|--quick Run "PRAGMA quick_check" on the database only. | |
| 957 | +** No other validation is performed. | |
| 952 | 958 | */ |
| 953 | 959 | void test_integrity(void){ |
| 954 | 960 | Stmt q; |
| 955 | 961 | Blob content; |
| 956 | 962 | int n1 = 0; |
| @@ -958,11 +964,25 @@ | ||
| 958 | 964 | int nErr = 0; |
| 959 | 965 | int total; |
| 960 | 966 | int nCA = 0; |
| 961 | 967 | int anCA[10]; |
| 962 | 968 | int bParse = find_option("parse",0,0)!=0; |
| 969 | + int bDbOnly = find_option("db-only","d",0)!=0; | |
| 970 | + int bQuick = find_option("quick","q",0)!=0; | |
| 963 | 971 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 2); |
| 972 | + if( bDbOnly || bQuick ){ | |
| 973 | + const char *zType = bQuick ? "quick" : "integrity"; | |
| 974 | + char *zRes; | |
| 975 | + zRes = db_text(0,"PRAGMA repository.%s_check", zType/*safe-for-%s*/); | |
| 976 | + if( fossil_strcmp(zRes,"ok")!=0 ){ | |
| 977 | + fossil_print("%s_check failed!\n", zType); | |
| 978 | + exit(1); | |
| 979 | + }else{ | |
| 980 | + fossil_print("ok\n"); | |
| 981 | + } | |
| 982 | + return; | |
| 983 | + } | |
| 964 | 984 | memset(anCA, 0, sizeof(anCA)); |
| 965 | 985 | |
| 966 | 986 | /* Make sure no public artifact is a delta from a private artifact */ |
| 967 | 987 | db_prepare(&q, |
| 968 | 988 | "SELECT " |
| 969 | 989 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -944,13 +944,19 @@ | |
| 944 | ** Verify that all content can be extracted from the BLOB table correctly. |
| 945 | ** If the BLOB table is correct, then the repository can always be |
| 946 | ** successfully reconstructed using "fossil rebuild". |
| 947 | ** |
| 948 | ** Options: |
| 949 | ** |
| 950 | ** --parse Parse all manifests, wikis, tickets, events, and |
| 951 | ** so forth, reporting any errors found. |
| 952 | */ |
| 953 | void test_integrity(void){ |
| 954 | Stmt q; |
| 955 | Blob content; |
| 956 | int n1 = 0; |
| @@ -958,11 +964,25 @@ | |
| 958 | int nErr = 0; |
| 959 | int total; |
| 960 | int nCA = 0; |
| 961 | int anCA[10]; |
| 962 | int bParse = find_option("parse",0,0)!=0; |
| 963 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 2); |
| 964 | memset(anCA, 0, sizeof(anCA)); |
| 965 | |
| 966 | /* Make sure no public artifact is a delta from a private artifact */ |
| 967 | db_prepare(&q, |
| 968 | "SELECT " |
| 969 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -944,13 +944,19 @@ | |
| 944 | ** Verify that all content can be extracted from the BLOB table correctly. |
| 945 | ** If the BLOB table is correct, then the repository can always be |
| 946 | ** successfully reconstructed using "fossil rebuild". |
| 947 | ** |
| 948 | ** Options: |
| 949 | ** |
| 950 | ** -d|--db-only Run "PRAGMA integrity_check" on the database only. |
| 951 | ** No other validation is performed. |
| 952 | ** |
| 953 | ** --parse Parse all manifests, wikis, tickets, events, and |
| 954 | ** so forth, reporting any errors found. |
| 955 | ** |
| 956 | ** -q|--quick Run "PRAGMA quick_check" on the database only. |
| 957 | ** No other validation is performed. |
| 958 | */ |
| 959 | void test_integrity(void){ |
| 960 | Stmt q; |
| 961 | Blob content; |
| 962 | int n1 = 0; |
| @@ -958,11 +964,25 @@ | |
| 964 | int nErr = 0; |
| 965 | int total; |
| 966 | int nCA = 0; |
| 967 | int anCA[10]; |
| 968 | int bParse = find_option("parse",0,0)!=0; |
| 969 | int bDbOnly = find_option("db-only","d",0)!=0; |
| 970 | int bQuick = find_option("quick","q",0)!=0; |
| 971 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 2); |
| 972 | if( bDbOnly || bQuick ){ |
| 973 | const char *zType = bQuick ? "quick" : "integrity"; |
| 974 | char *zRes; |
| 975 | zRes = db_text(0,"PRAGMA repository.%s_check", zType/*safe-for-%s*/); |
| 976 | if( fossil_strcmp(zRes,"ok")!=0 ){ |
| 977 | fossil_print("%s_check failed!\n", zType); |
| 978 | exit(1); |
| 979 | }else{ |
| 980 | fossil_print("ok\n"); |
| 981 | } |
| 982 | return; |
| 983 | } |
| 984 | memset(anCA, 0, sizeof(anCA)); |
| 985 | |
| 986 | /* Make sure no public artifact is a delta from a private artifact */ |
| 987 | db_prepare(&q, |
| 988 | "SELECT " |
| 989 |
+19
-14
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -810,11 +810,11 @@ | ||
| 810 | 810 | ** WEBPAGE: doc |
| 811 | 811 | ** URL: /uv/FILE |
| 812 | 812 | ** URL: /doc/CHECKIN/FILE |
| 813 | 813 | ** |
| 814 | 814 | ** CHECKIN can be either tag or hash prefix or timestamp identifying a |
| 815 | -** particular check, or the name of a branch (meaning the most recent | |
| 815 | +** particular check-in, or the name of a branch (meaning the most recent | |
| 816 | 816 | ** check-in on that branch) or one of various magic words: |
| 817 | 817 | ** |
| 818 | 818 | ** "tip" means the most recent check-in |
| 819 | 819 | ** |
| 820 | 820 | ** "ckout" means the current check-out, if the server is run from |
| @@ -824,11 +824,12 @@ | ||
| 824 | 824 | ** regardless of what branch it occurs on. |
| 825 | 825 | ** |
| 826 | 826 | ** FILE is the name of a file to delivered up as a webpage. FILE is relative |
| 827 | 827 | ** to the root of the source tree of the repository. The FILE must |
| 828 | 828 | ** be a part of CHECKIN, except when CHECKIN=="ckout" when FILE is read |
| 829 | -** directly from disk and need not be a managed file. | |
| 829 | +** directly from disk and need not be a managed file. For /uv, FILE | |
| 830 | +** can also be the hash of the unversioned file. | |
| 830 | 831 | ** |
| 831 | 832 | ** The "ckout" CHECKIN is intended for development - to provide a mechanism |
| 832 | 833 | ** for looking at what a file will look like using the /doc webpage after |
| 833 | 834 | ** it gets checked in. |
| 834 | 835 | ** |
| @@ -931,22 +932,26 @@ | ||
| 931 | 932 | goto doc_not_found; |
| 932 | 933 | } |
| 933 | 934 | } |
| 934 | 935 | if( isUV ){ |
| 935 | 936 | if( db_table_exists("repository","unversioned") ){ |
| 936 | - Stmt q; | |
| 937 | - db_prepare(&q, "SELECT hash, mtime FROM unversioned" | |
| 938 | - " WHERE name=%Q", zName); | |
| 939 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 940 | - etag_check(ETAG_HASH, db_column_text(&q,0)); | |
| 941 | - etag_last_modified(db_column_int64(&q,1)); | |
| 942 | - } | |
| 943 | - db_finalize(&q); | |
| 944 | - if( unversioned_content(zName, &filebody)==0 ){ | |
| 945 | - rid = 1; | |
| 946 | - zDfltTitle = zName; | |
| 947 | - } | |
| 937 | + rid = unversioned_content(zName, &filebody); | |
| 938 | + if( rid==1 ){ | |
| 939 | + Stmt q; | |
| 940 | + db_prepare(&q, "SELECT hash, mtime FROM unversioned" | |
| 941 | + " WHERE name=%Q", zName); | |
| 942 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 943 | + etag_check(ETAG_HASH, db_column_text(&q,0)); | |
| 944 | + etag_last_modified(db_column_int64(&q,1)); | |
| 945 | + } | |
| 946 | + db_finalize(&q); | |
| 947 | + }else if( rid==2 ){ | |
| 948 | + zName = db_text(zName, | |
| 949 | + "SELECT name FROM unversioned WHERE hash=%Q", zName); | |
| 950 | + g.isConst = 1; | |
| 951 | + } | |
| 952 | + zDfltTitle = zName; | |
| 948 | 953 | } |
| 949 | 954 | }else if( fossil_strcmp(zCheckin,"ckout")==0 ){ |
| 950 | 955 | /* Read from the local checkout */ |
| 951 | 956 | char *zFullpath; |
| 952 | 957 | db_must_be_within_tree(); |
| 953 | 958 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -810,11 +810,11 @@ | |
| 810 | ** WEBPAGE: doc |
| 811 | ** URL: /uv/FILE |
| 812 | ** URL: /doc/CHECKIN/FILE |
| 813 | ** |
| 814 | ** CHECKIN can be either tag or hash prefix or timestamp identifying a |
| 815 | ** particular check, or the name of a branch (meaning the most recent |
| 816 | ** check-in on that branch) or one of various magic words: |
| 817 | ** |
| 818 | ** "tip" means the most recent check-in |
| 819 | ** |
| 820 | ** "ckout" means the current check-out, if the server is run from |
| @@ -824,11 +824,12 @@ | |
| 824 | ** regardless of what branch it occurs on. |
| 825 | ** |
| 826 | ** FILE is the name of a file to delivered up as a webpage. FILE is relative |
| 827 | ** to the root of the source tree of the repository. The FILE must |
| 828 | ** be a part of CHECKIN, except when CHECKIN=="ckout" when FILE is read |
| 829 | ** directly from disk and need not be a managed file. |
| 830 | ** |
| 831 | ** The "ckout" CHECKIN is intended for development - to provide a mechanism |
| 832 | ** for looking at what a file will look like using the /doc webpage after |
| 833 | ** it gets checked in. |
| 834 | ** |
| @@ -931,22 +932,26 @@ | |
| 931 | goto doc_not_found; |
| 932 | } |
| 933 | } |
| 934 | if( isUV ){ |
| 935 | if( db_table_exists("repository","unversioned") ){ |
| 936 | Stmt q; |
| 937 | db_prepare(&q, "SELECT hash, mtime FROM unversioned" |
| 938 | " WHERE name=%Q", zName); |
| 939 | if( db_step(&q)==SQLITE_ROW ){ |
| 940 | etag_check(ETAG_HASH, db_column_text(&q,0)); |
| 941 | etag_last_modified(db_column_int64(&q,1)); |
| 942 | } |
| 943 | db_finalize(&q); |
| 944 | if( unversioned_content(zName, &filebody)==0 ){ |
| 945 | rid = 1; |
| 946 | zDfltTitle = zName; |
| 947 | } |
| 948 | } |
| 949 | }else if( fossil_strcmp(zCheckin,"ckout")==0 ){ |
| 950 | /* Read from the local checkout */ |
| 951 | char *zFullpath; |
| 952 | db_must_be_within_tree(); |
| 953 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -810,11 +810,11 @@ | |
| 810 | ** WEBPAGE: doc |
| 811 | ** URL: /uv/FILE |
| 812 | ** URL: /doc/CHECKIN/FILE |
| 813 | ** |
| 814 | ** CHECKIN can be either tag or hash prefix or timestamp identifying a |
| 815 | ** particular check-in, or the name of a branch (meaning the most recent |
| 816 | ** check-in on that branch) or one of various magic words: |
| 817 | ** |
| 818 | ** "tip" means the most recent check-in |
| 819 | ** |
| 820 | ** "ckout" means the current check-out, if the server is run from |
| @@ -824,11 +824,12 @@ | |
| 824 | ** regardless of what branch it occurs on. |
| 825 | ** |
| 826 | ** FILE is the name of a file to delivered up as a webpage. FILE is relative |
| 827 | ** to the root of the source tree of the repository. The FILE must |
| 828 | ** be a part of CHECKIN, except when CHECKIN=="ckout" when FILE is read |
| 829 | ** directly from disk and need not be a managed file. For /uv, FILE |
| 830 | ** can also be the hash of the unversioned file. |
| 831 | ** |
| 832 | ** The "ckout" CHECKIN is intended for development - to provide a mechanism |
| 833 | ** for looking at what a file will look like using the /doc webpage after |
| 834 | ** it gets checked in. |
| 835 | ** |
| @@ -931,22 +932,26 @@ | |
| 932 | goto doc_not_found; |
| 933 | } |
| 934 | } |
| 935 | if( isUV ){ |
| 936 | if( db_table_exists("repository","unversioned") ){ |
| 937 | rid = unversioned_content(zName, &filebody); |
| 938 | if( rid==1 ){ |
| 939 | Stmt q; |
| 940 | db_prepare(&q, "SELECT hash, mtime FROM unversioned" |
| 941 | " WHERE name=%Q", zName); |
| 942 | if( db_step(&q)==SQLITE_ROW ){ |
| 943 | etag_check(ETAG_HASH, db_column_text(&q,0)); |
| 944 | etag_last_modified(db_column_int64(&q,1)); |
| 945 | } |
| 946 | db_finalize(&q); |
| 947 | }else if( rid==2 ){ |
| 948 | zName = db_text(zName, |
| 949 | "SELECT name FROM unversioned WHERE hash=%Q", zName); |
| 950 | g.isConst = 1; |
| 951 | } |
| 952 | zDfltTitle = zName; |
| 953 | } |
| 954 | }else if( fossil_strcmp(zCheckin,"ckout")==0 ){ |
| 955 | /* Read from the local checkout */ |
| 956 | char *zFullpath; |
| 957 | db_must_be_within_tree(); |
| 958 |
+4
-3
| --- src/finfo.c | ||
| +++ src/finfo.c | ||
| @@ -438,12 +438,13 @@ | ||
| 438 | 438 | }else if( n>0 ){ |
| 439 | 439 | blob_appendf(&title, "First %d ancestors of file ", n); |
| 440 | 440 | }else{ |
| 441 | 441 | blob_appendf(&title, "Ancestors of file "); |
| 442 | 442 | } |
| 443 | - blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>", | |
| 444 | - zFilename, zFilename); | |
| 443 | + blob_appendf(&title,"%z%h</a>", | |
| 444 | + href("%R/file?name=%T&ci=%!S", zFilename, zUuid), | |
| 445 | + zFilename); | |
| 445 | 446 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 446 | 447 | blob_append(&title, origCheckin ? " between " : " from ", -1); |
| 447 | 448 | blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid); |
| 448 | 449 | if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin); |
| 449 | 450 | fossil_free(zUuid); |
| @@ -453,11 +454,11 @@ | ||
| 453 | 454 | blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid); |
| 454 | 455 | fossil_free(zUuid); |
| 455 | 456 | } |
| 456 | 457 | }else{ |
| 457 | 458 | blob_appendf(&title, "History for "); |
| 458 | - hyperlinked_path(zFilename, &title, 0, "tree", ""); | |
| 459 | + hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE); | |
| 459 | 460 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 460 | 461 | } |
| 461 | 462 | if( uBg ){ |
| 462 | 463 | blob_append(&title, " (color-coded by user)", -1); |
| 463 | 464 | } |
| 464 | 465 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -438,12 +438,13 @@ | |
| 438 | }else if( n>0 ){ |
| 439 | blob_appendf(&title, "First %d ancestors of file ", n); |
| 440 | }else{ |
| 441 | blob_appendf(&title, "Ancestors of file "); |
| 442 | } |
| 443 | blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>", |
| 444 | zFilename, zFilename); |
| 445 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 446 | blob_append(&title, origCheckin ? " between " : " from ", -1); |
| 447 | blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid); |
| 448 | if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin); |
| 449 | fossil_free(zUuid); |
| @@ -453,11 +454,11 @@ | |
| 453 | blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid); |
| 454 | fossil_free(zUuid); |
| 455 | } |
| 456 | }else{ |
| 457 | blob_appendf(&title, "History for "); |
| 458 | hyperlinked_path(zFilename, &title, 0, "tree", ""); |
| 459 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 460 | } |
| 461 | if( uBg ){ |
| 462 | blob_append(&title, " (color-coded by user)", -1); |
| 463 | } |
| 464 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -438,12 +438,13 @@ | |
| 438 | }else if( n>0 ){ |
| 439 | blob_appendf(&title, "First %d ancestors of file ", n); |
| 440 | }else{ |
| 441 | blob_appendf(&title, "Ancestors of file "); |
| 442 | } |
| 443 | blob_appendf(&title,"%z%h</a>", |
| 444 | href("%R/file?name=%T&ci=%!S", zFilename, zUuid), |
| 445 | zFilename); |
| 446 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 447 | blob_append(&title, origCheckin ? " between " : " from ", -1); |
| 448 | blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid); |
| 449 | if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin); |
| 450 | fossil_free(zUuid); |
| @@ -453,11 +454,11 @@ | |
| 454 | blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid); |
| 455 | fossil_free(zUuid); |
| 456 | } |
| 457 | }else{ |
| 458 | blob_appendf(&title, "History for "); |
| 459 | hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE); |
| 460 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 461 | } |
| 462 | if( uBg ){ |
| 463 | blob_append(&title, " (color-coded by user)", -1); |
| 464 | } |
| 465 |
+5
-4
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -1013,12 +1013,12 @@ | ||
| 1013 | 1013 | @ Title: <input type="input" name="title" value="%h(zTitle)" size="50" |
| 1014 | 1014 | @ maxlength="125"><br> |
| 1015 | 1015 | } |
| 1016 | 1016 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 1017 | 1017 | mimetype_option_menu(zMimetype); |
| 1018 | - @ <br><textarea name="content" class="wikiedit" cols="80" \ | |
| 1019 | - @ rows="25" wrap="virtual">%h(zContent)</textarea><br> | |
| 1018 | + @ <br><textarea aria-label="Content:" name="content" class="wikiedit" \ | |
| 1019 | + @ cols="80" rows="25" wrap="virtual">%h(zContent)</textarea><br> | |
| 1020 | 1020 | } |
| 1021 | 1021 | |
| 1022 | 1022 | /* |
| 1023 | 1023 | ** WEBPAGE: forumnew |
| 1024 | 1024 | ** WEBPAGE: forumedit |
| @@ -1119,11 +1119,12 @@ | ||
| 1119 | 1119 | @ <input type="submit" name="submit" value="Submit"> |
| 1120 | 1120 | }else{ |
| 1121 | 1121 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 1122 | 1122 | } |
| 1123 | 1123 | if( g.perm.Debug ){ |
| 1124 | - /* For the test-forumnew page add these extra debugging controls */ | |
| 1124 | + /* Give extra control over the post to users with the special | |
| 1125 | + * Debug capability, which includes Admin and Setup users */ | |
| 1125 | 1126 | @ <div class="debug"> |
| 1126 | 1127 | @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ |
| 1127 | 1128 | @ Dry run</label> |
| 1128 | 1129 | @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ |
| 1129 | 1130 | @ Require moderator approval</label> |
| @@ -1235,11 +1236,11 @@ | ||
| 1235 | 1236 | @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> |
| 1236 | 1237 | @ <input type="hidden" name="nullout" value="1"> |
| 1237 | 1238 | @ <input type="hidden" name="mimetype" value="%h(zMimetype)"> |
| 1238 | 1239 | @ <input type="hidden" name="content" value="%h(zContent)"> |
| 1239 | 1240 | if( zTitle ){ |
| 1240 | - @ <input type="hidden" name="title" value="%h(zTitle)"> | |
| 1241 | + @ <input aria-label="Title" type="hidden" name="title" value="%h(zTitle)"> | |
| 1241 | 1242 | } |
| 1242 | 1243 | }else if( P("edit") ){ |
| 1243 | 1244 | /* Provide an edit to the fpid post */ |
| 1244 | 1245 | zMimetype = P("mimetype"); |
| 1245 | 1246 | zContent = PT("content"); |
| 1246 | 1247 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -1013,12 +1013,12 @@ | |
| 1013 | @ Title: <input type="input" name="title" value="%h(zTitle)" size="50" |
| 1014 | @ maxlength="125"><br> |
| 1015 | } |
| 1016 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 1017 | mimetype_option_menu(zMimetype); |
| 1018 | @ <br><textarea name="content" class="wikiedit" cols="80" \ |
| 1019 | @ rows="25" wrap="virtual">%h(zContent)</textarea><br> |
| 1020 | } |
| 1021 | |
| 1022 | /* |
| 1023 | ** WEBPAGE: forumnew |
| 1024 | ** WEBPAGE: forumedit |
| @@ -1119,11 +1119,12 @@ | |
| 1119 | @ <input type="submit" name="submit" value="Submit"> |
| 1120 | }else{ |
| 1121 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 1122 | } |
| 1123 | if( g.perm.Debug ){ |
| 1124 | /* For the test-forumnew page add these extra debugging controls */ |
| 1125 | @ <div class="debug"> |
| 1126 | @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ |
| 1127 | @ Dry run</label> |
| 1128 | @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ |
| 1129 | @ Require moderator approval</label> |
| @@ -1235,11 +1236,11 @@ | |
| 1235 | @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> |
| 1236 | @ <input type="hidden" name="nullout" value="1"> |
| 1237 | @ <input type="hidden" name="mimetype" value="%h(zMimetype)"> |
| 1238 | @ <input type="hidden" name="content" value="%h(zContent)"> |
| 1239 | if( zTitle ){ |
| 1240 | @ <input type="hidden" name="title" value="%h(zTitle)"> |
| 1241 | } |
| 1242 | }else if( P("edit") ){ |
| 1243 | /* Provide an edit to the fpid post */ |
| 1244 | zMimetype = P("mimetype"); |
| 1245 | zContent = PT("content"); |
| 1246 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -1013,12 +1013,12 @@ | |
| 1013 | @ Title: <input type="input" name="title" value="%h(zTitle)" size="50" |
| 1014 | @ maxlength="125"><br> |
| 1015 | } |
| 1016 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 1017 | mimetype_option_menu(zMimetype); |
| 1018 | @ <br><textarea aria-label="Content:" name="content" class="wikiedit" \ |
| 1019 | @ cols="80" rows="25" wrap="virtual">%h(zContent)</textarea><br> |
| 1020 | } |
| 1021 | |
| 1022 | /* |
| 1023 | ** WEBPAGE: forumnew |
| 1024 | ** WEBPAGE: forumedit |
| @@ -1119,11 +1119,12 @@ | |
| 1119 | @ <input type="submit" name="submit" value="Submit"> |
| 1120 | }else{ |
| 1121 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 1122 | } |
| 1123 | if( g.perm.Debug ){ |
| 1124 | /* Give extra control over the post to users with the special |
| 1125 | * Debug capability, which includes Admin and Setup users */ |
| 1126 | @ <div class="debug"> |
| 1127 | @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ |
| 1128 | @ Dry run</label> |
| 1129 | @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ |
| 1130 | @ Require moderator approval</label> |
| @@ -1235,11 +1236,11 @@ | |
| 1236 | @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> |
| 1237 | @ <input type="hidden" name="nullout" value="1"> |
| 1238 | @ <input type="hidden" name="mimetype" value="%h(zMimetype)"> |
| 1239 | @ <input type="hidden" name="content" value="%h(zContent)"> |
| 1240 | if( zTitle ){ |
| 1241 | @ <input aria-label="Title" type="hidden" name="title" value="%h(zTitle)"> |
| 1242 | } |
| 1243 | }else if( P("edit") ){ |
| 1244 | /* Provide an edit to the fpid post */ |
| 1245 | zMimetype = P("mimetype"); |
| 1246 | zContent = PT("content"); |
| 1247 |
+14
-6
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -24,19 +24,19 @@ | ||
| 24 | 24 | ** SSL support is abstracted out into this module because Fossil can |
| 25 | 25 | ** be compiled without SSL support (which requires OpenSSL library) |
| 26 | 26 | */ |
| 27 | 27 | |
| 28 | 28 | #include "config.h" |
| 29 | +#include "http_ssl.h" | |
| 29 | 30 | |
| 30 | 31 | #ifdef FOSSIL_ENABLE_SSL |
| 31 | 32 | |
| 32 | 33 | #include <openssl/bio.h> |
| 33 | 34 | #include <openssl/ssl.h> |
| 34 | 35 | #include <openssl/err.h> |
| 35 | 36 | #include <openssl/x509.h> |
| 36 | 37 | |
| 37 | -#include "http_ssl.h" | |
| 38 | 38 | #include <assert.h> |
| 39 | 39 | #include <sys/types.h> |
| 40 | 40 | |
| 41 | 41 | /* |
| 42 | 42 | ** There can only be a single OpenSSL IO connection open at a time. |
| @@ -328,10 +328,11 @@ | ||
| 328 | 328 | ssl_close(); |
| 329 | 329 | return 1; |
| 330 | 330 | } |
| 331 | 331 | |
| 332 | 332 | if( !sslNoCertVerify && SSL_get_verify_result(ssl)!=X509_V_OK ){ |
| 333 | + int x; | |
| 333 | 334 | char *desc, *prompt; |
| 334 | 335 | Blob ans; |
| 335 | 336 | char cReply; |
| 336 | 337 | BIO *mem; |
| 337 | 338 | unsigned char md[32]; |
| @@ -338,11 +339,17 @@ | ||
| 338 | 339 | char zHash[32*2+1]; |
| 339 | 340 | unsigned int mdLength = (int)sizeof(md); |
| 340 | 341 | |
| 341 | 342 | memset(md, 0, sizeof(md)); |
| 342 | 343 | zHash[0] = 0; |
| 343 | - if( X509_digest(cert, EVP_sha256(), md, &mdLength) ){ | |
| 344 | + /* MMNNFFPPS */ | |
| 345 | +#if OPENSSL_VERSION_NUMBER >= 0x010000000 | |
| 346 | + x = X509_digest(cert, EVP_sha256(), md, &mdLength); | |
| 347 | +#else | |
| 348 | + x = X509_digest(cert, EVP_sha1(), md, &mdLength); | |
| 349 | +#endif | |
| 350 | + if( x ){ | |
| 344 | 351 | int j; |
| 345 | 352 | for(j=0; j<mdLength && j*2+1<sizeof(zHash); ++j){ |
| 346 | 353 | zHash[j*2] = "0123456789abcdef"[md[j]>>4]; |
| 347 | 354 | zHash[j*2+1] = "0123456789abcdef"[md[j]&0xf]; |
| 348 | 355 | } |
| @@ -518,25 +525,26 @@ | ||
| 518 | 525 | ** for the domains listed. Or if |
| 519 | 526 | ** the --all option is specified, |
| 520 | 527 | ** remove all TLS cert exceptions. |
| 521 | 528 | */ |
| 522 | 529 | void test_tlsconfig_info(void){ |
| 523 | - const char *zCmd; | |
| 524 | - size_t nCmd; | |
| 525 | - int nHit = 0; | |
| 526 | 530 | #if !defined(FOSSIL_ENABLE_SSL) |
| 527 | 531 | fossil_print("TLS disabled in this build\n"); |
| 528 | 532 | #else |
| 533 | + const char *zCmd; | |
| 534 | + size_t nCmd; | |
| 535 | + int nHit = 0; | |
| 529 | 536 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 530 | 537 | db_open_config(1,0); |
| 531 | 538 | zCmd = g.argc>=3 ? g.argv[2] : "show"; |
| 532 | 539 | nCmd = strlen(zCmd); |
| 533 | 540 | if( strncmp("show",zCmd,nCmd)==0 ){ |
| 534 | 541 | const char *zName, *zValue; |
| 535 | 542 | size_t nName; |
| 536 | 543 | Stmt q; |
| 537 | - fossil_print("OpenSSL-version: %s\n", SSLeay_version(SSLEAY_VERSION)); | |
| 544 | + fossil_print("OpenSSL-version: %s (0x%09x)\n", | |
| 545 | + SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER); | |
| 538 | 546 | fossil_print("OpenSSL-cert-file: %s\n", X509_get_default_cert_file()); |
| 539 | 547 | fossil_print("OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir()); |
| 540 | 548 | zName = X509_get_default_cert_file_env(); |
| 541 | 549 | zValue = fossil_getenv(zName); |
| 542 | 550 | if( zValue==0 ) zValue = ""; |
| 543 | 551 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -24,19 +24,19 @@ | |
| 24 | ** SSL support is abstracted out into this module because Fossil can |
| 25 | ** be compiled without SSL support (which requires OpenSSL library) |
| 26 | */ |
| 27 | |
| 28 | #include "config.h" |
| 29 | |
| 30 | #ifdef FOSSIL_ENABLE_SSL |
| 31 | |
| 32 | #include <openssl/bio.h> |
| 33 | #include <openssl/ssl.h> |
| 34 | #include <openssl/err.h> |
| 35 | #include <openssl/x509.h> |
| 36 | |
| 37 | #include "http_ssl.h" |
| 38 | #include <assert.h> |
| 39 | #include <sys/types.h> |
| 40 | |
| 41 | /* |
| 42 | ** There can only be a single OpenSSL IO connection open at a time. |
| @@ -328,10 +328,11 @@ | |
| 328 | ssl_close(); |
| 329 | return 1; |
| 330 | } |
| 331 | |
| 332 | if( !sslNoCertVerify && SSL_get_verify_result(ssl)!=X509_V_OK ){ |
| 333 | char *desc, *prompt; |
| 334 | Blob ans; |
| 335 | char cReply; |
| 336 | BIO *mem; |
| 337 | unsigned char md[32]; |
| @@ -338,11 +339,17 @@ | |
| 338 | char zHash[32*2+1]; |
| 339 | unsigned int mdLength = (int)sizeof(md); |
| 340 | |
| 341 | memset(md, 0, sizeof(md)); |
| 342 | zHash[0] = 0; |
| 343 | if( X509_digest(cert, EVP_sha256(), md, &mdLength) ){ |
| 344 | int j; |
| 345 | for(j=0; j<mdLength && j*2+1<sizeof(zHash); ++j){ |
| 346 | zHash[j*2] = "0123456789abcdef"[md[j]>>4]; |
| 347 | zHash[j*2+1] = "0123456789abcdef"[md[j]&0xf]; |
| 348 | } |
| @@ -518,25 +525,26 @@ | |
| 518 | ** for the domains listed. Or if |
| 519 | ** the --all option is specified, |
| 520 | ** remove all TLS cert exceptions. |
| 521 | */ |
| 522 | void test_tlsconfig_info(void){ |
| 523 | const char *zCmd; |
| 524 | size_t nCmd; |
| 525 | int nHit = 0; |
| 526 | #if !defined(FOSSIL_ENABLE_SSL) |
| 527 | fossil_print("TLS disabled in this build\n"); |
| 528 | #else |
| 529 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 530 | db_open_config(1,0); |
| 531 | zCmd = g.argc>=3 ? g.argv[2] : "show"; |
| 532 | nCmd = strlen(zCmd); |
| 533 | if( strncmp("show",zCmd,nCmd)==0 ){ |
| 534 | const char *zName, *zValue; |
| 535 | size_t nName; |
| 536 | Stmt q; |
| 537 | fossil_print("OpenSSL-version: %s\n", SSLeay_version(SSLEAY_VERSION)); |
| 538 | fossil_print("OpenSSL-cert-file: %s\n", X509_get_default_cert_file()); |
| 539 | fossil_print("OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir()); |
| 540 | zName = X509_get_default_cert_file_env(); |
| 541 | zValue = fossil_getenv(zName); |
| 542 | if( zValue==0 ) zValue = ""; |
| 543 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -24,19 +24,19 @@ | |
| 24 | ** SSL support is abstracted out into this module because Fossil can |
| 25 | ** be compiled without SSL support (which requires OpenSSL library) |
| 26 | */ |
| 27 | |
| 28 | #include "config.h" |
| 29 | #include "http_ssl.h" |
| 30 | |
| 31 | #ifdef FOSSIL_ENABLE_SSL |
| 32 | |
| 33 | #include <openssl/bio.h> |
| 34 | #include <openssl/ssl.h> |
| 35 | #include <openssl/err.h> |
| 36 | #include <openssl/x509.h> |
| 37 | |
| 38 | #include <assert.h> |
| 39 | #include <sys/types.h> |
| 40 | |
| 41 | /* |
| 42 | ** There can only be a single OpenSSL IO connection open at a time. |
| @@ -328,10 +328,11 @@ | |
| 328 | ssl_close(); |
| 329 | return 1; |
| 330 | } |
| 331 | |
| 332 | if( !sslNoCertVerify && SSL_get_verify_result(ssl)!=X509_V_OK ){ |
| 333 | int x; |
| 334 | char *desc, *prompt; |
| 335 | Blob ans; |
| 336 | char cReply; |
| 337 | BIO *mem; |
| 338 | unsigned char md[32]; |
| @@ -338,11 +339,17 @@ | |
| 339 | char zHash[32*2+1]; |
| 340 | unsigned int mdLength = (int)sizeof(md); |
| 341 | |
| 342 | memset(md, 0, sizeof(md)); |
| 343 | zHash[0] = 0; |
| 344 | /* MMNNFFPPS */ |
| 345 | #if OPENSSL_VERSION_NUMBER >= 0x010000000 |
| 346 | x = X509_digest(cert, EVP_sha256(), md, &mdLength); |
| 347 | #else |
| 348 | x = X509_digest(cert, EVP_sha1(), md, &mdLength); |
| 349 | #endif |
| 350 | if( x ){ |
| 351 | int j; |
| 352 | for(j=0; j<mdLength && j*2+1<sizeof(zHash); ++j){ |
| 353 | zHash[j*2] = "0123456789abcdef"[md[j]>>4]; |
| 354 | zHash[j*2+1] = "0123456789abcdef"[md[j]&0xf]; |
| 355 | } |
| @@ -518,25 +525,26 @@ | |
| 525 | ** for the domains listed. Or if |
| 526 | ** the --all option is specified, |
| 527 | ** remove all TLS cert exceptions. |
| 528 | */ |
| 529 | void test_tlsconfig_info(void){ |
| 530 | #if !defined(FOSSIL_ENABLE_SSL) |
| 531 | fossil_print("TLS disabled in this build\n"); |
| 532 | #else |
| 533 | const char *zCmd; |
| 534 | size_t nCmd; |
| 535 | int nHit = 0; |
| 536 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 537 | db_open_config(1,0); |
| 538 | zCmd = g.argc>=3 ? g.argv[2] : "show"; |
| 539 | nCmd = strlen(zCmd); |
| 540 | if( strncmp("show",zCmd,nCmd)==0 ){ |
| 541 | const char *zName, *zValue; |
| 542 | size_t nName; |
| 543 | Stmt q; |
| 544 | fossil_print("OpenSSL-version: %s (0x%09x)\n", |
| 545 | SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER); |
| 546 | fossil_print("OpenSSL-cert-file: %s\n", X509_get_default_cert_file()); |
| 547 | fossil_print("OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir()); |
| 548 | zName = X509_get_default_cert_file_env(); |
| 549 | zValue = fossil_getenv(zName); |
| 550 | if( zValue==0 ) zValue = ""; |
| 551 |
+147
-147
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -666,10 +666,11 @@ | ||
| 666 | 666 | const char *zOrigDate; |
| 667 | 667 | int okWiki = 0; |
| 668 | 668 | Blob wiki_read_links = BLOB_INITIALIZER; |
| 669 | 669 | Blob wiki_add_links = BLOB_INITIALIZER; |
| 670 | 670 | |
| 671 | + Th_Store("current_checkin", zName); | |
| 671 | 672 | style_header("Check-in [%S]", zUuid); |
| 672 | 673 | login_anonymous_available(); |
| 673 | 674 | zEUser = db_text(0, |
| 674 | 675 | "SELECT value FROM tagxref" |
| 675 | 676 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| @@ -1661,12 +1662,12 @@ | ||
| 1661 | 1662 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1662 | 1663 | cookie_link_parameter("diff","diff","2"); |
| 1663 | 1664 | diffType = atoi(PD("diff","2")); |
| 1664 | 1665 | cookie_render(); |
| 1665 | 1666 | if( P("from") && P("to") ){ |
| 1666 | - v1 = artifact_from_ci_and_filename(0, "from"); | |
| 1667 | - v2 = artifact_from_ci_and_filename(0, "to"); | |
| 1667 | + v1 = artifact_from_ci_and_filename("from"); | |
| 1668 | + v2 = artifact_from_ci_and_filename("to"); | |
| 1668 | 1669 | }else{ |
| 1669 | 1670 | Stmt q; |
| 1670 | 1671 | v1 = name_to_rid_www("v1"); |
| 1671 | 1672 | v2 = name_to_rid_www("v2"); |
| 1672 | 1673 | |
| @@ -1763,12 +1764,12 @@ | ||
| 1763 | 1764 | */ |
| 1764 | 1765 | void rawartifact_page(void){ |
| 1765 | 1766 | int rid = 0; |
| 1766 | 1767 | char *zUuid; |
| 1767 | 1768 | |
| 1768 | - if( P("ci") && P("filename") ){ | |
| 1769 | - rid = artifact_from_ci_and_filename(0, 0); | |
| 1769 | + if( P("ci") ){ | |
| 1770 | + rid = artifact_from_ci_and_filename(0); | |
| 1770 | 1771 | } |
| 1771 | 1772 | if( rid==0 ){ |
| 1772 | 1773 | rid = name_to_rid_www("name"); |
| 1773 | 1774 | } |
| 1774 | 1775 | login_check_credentials(); |
| @@ -1942,59 +1943,53 @@ | ||
| 1942 | 1943 | |
| 1943 | 1944 | /* |
| 1944 | 1945 | ** Look for "ci" and "filename" query parameters. If found, try to |
| 1945 | 1946 | ** use them to extract the record ID of an artifact for the file. |
| 1946 | 1947 | ** |
| 1947 | -** Also look for "fn" as an alias for "filename". If either "filename" | |
| 1948 | -** or "fn" is present but "ci" is missing, use "tip" as a default value | |
| 1949 | -** for "ci". | |
| 1950 | -** | |
| 1951 | -** If zNameParam is not NULL, this use that parameter as the filename | |
| 1952 | -** rather than "fn" or "filename". | |
| 1953 | -** | |
| 1954 | -** If pUrl is not NULL, then record the "ci" and "filename" values in | |
| 1955 | -** pUrl. | |
| 1956 | -** | |
| 1957 | -** At least one of pUrl or zNameParam must be NULL. | |
| 1948 | +** Also look for "fn" and "name" as an aliases for "filename". If any | |
| 1949 | +** "filename" or "fn" or "name" are present but "ci" is missing, then | |
| 1950 | +** use "tip" as the default value for "ci". | |
| 1951 | +** | |
| 1952 | +** If zNameParam is not NULL, then use that parameter as the filename | |
| 1953 | +** rather than "fn" or "filename" or "name". the zNameParam is used | |
| 1954 | +** for the from= and to= query parameters of /fdiff. | |
| 1958 | 1955 | */ |
| 1959 | -int artifact_from_ci_and_filename(HQuery *pUrl, const char *zNameParam){ | |
| 1956 | +int artifact_from_ci_and_filename(const char *zNameParam){ | |
| 1960 | 1957 | const char *zFilename; |
| 1961 | 1958 | const char *zCI; |
| 1962 | 1959 | int cirid; |
| 1963 | 1960 | Manifest *pManifest; |
| 1964 | 1961 | ManifestFile *pFile; |
| 1962 | + int rid = 0; | |
| 1965 | 1963 | |
| 1966 | 1964 | if( zNameParam ){ |
| 1967 | 1965 | zFilename = P(zNameParam); |
| 1968 | 1966 | }else{ |
| 1969 | 1967 | zFilename = P("filename"); |
| 1970 | 1968 | if( zFilename==0 ){ |
| 1971 | 1969 | zFilename = P("fn"); |
| 1972 | 1970 | } |
| 1971 | + if( zFilename==0 ){ | |
| 1972 | + zFilename = P("name"); | |
| 1973 | + } | |
| 1973 | 1974 | } |
| 1974 | 1975 | if( zFilename==0 ) return 0; |
| 1975 | 1976 | |
| 1976 | - zCI = P("ci"); | |
| 1977 | - cirid = name_to_typed_rid(zCI ? zCI : "tip", "ci"); | |
| 1977 | + zCI = PD("ci", "tip"); | |
| 1978 | + cirid = name_to_typed_rid(zCI, "ci"); | |
| 1978 | 1979 | if( cirid<=0 ) return 0; |
| 1979 | 1980 | pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0); |
| 1980 | 1981 | if( pManifest==0 ) return 0; |
| 1981 | 1982 | manifest_file_rewind(pManifest); |
| 1982 | 1983 | while( (pFile = manifest_file_next(pManifest,0))!=0 ){ |
| 1983 | 1984 | if( fossil_strcmp(zFilename, pFile->zName)==0 ){ |
| 1984 | - int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); | |
| 1985 | - manifest_destroy(pManifest); | |
| 1986 | - if( pUrl ){ | |
| 1987 | - assert( zNameParam==0 ); | |
| 1988 | - url_add_parameter(pUrl, "fn", zFilename); | |
| 1989 | - if( zCI ) url_add_parameter(pUrl, "ci", zCI); | |
| 1990 | - } | |
| 1991 | - return rid; | |
| 1985 | + rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); | |
| 1986 | + break; | |
| 1992 | 1987 | } |
| 1993 | 1988 | } |
| 1994 | 1989 | manifest_destroy(pManifest); |
| 1995 | - return 0; | |
| 1990 | + return rid; | |
| 1996 | 1991 | } |
| 1997 | 1992 | |
| 1998 | 1993 | /* |
| 1999 | 1994 | ** The "z" argument is a string that contains the text of a source code |
| 2000 | 1995 | ** file. This routine appends that text to the HTTP reply with line numbering. |
| @@ -2096,21 +2091,31 @@ | ||
| 2096 | 2091 | ** ln=N - highlight line number N |
| 2097 | 2092 | ** ln=M-N - highlight lines M through N inclusive |
| 2098 | 2093 | ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive) |
| 2099 | 2094 | ** verbose - show more detail in the description |
| 2100 | 2095 | ** download - redirect to the download (artifact page only) |
| 2101 | -** name=NAME - Provide filename or hash as a query parameter | |
| 2096 | +** name=NAME - filename or hash as a query parameter | |
| 2102 | 2097 | ** filename=NAME - alternative spelling for "name=" |
| 2103 | 2098 | ** fn=NAME - alternative spelling for "name=" |
| 2104 | -** ci=VERSION - The specific check-in to use for "filename=". | |
| 2099 | +** ci=VERSION - The specific check-in to use with "name=" to | |
| 2100 | +** identify the file. | |
| 2105 | 2101 | ** |
| 2106 | 2102 | ** The /artifact page show the complete content of a file |
| 2107 | -** identified by HASH as preformatted text. The | |
| 2108 | -** /whatis page shows only a description of the file. The /file | |
| 2109 | -** page shows the most recent version of the file or directory | |
| 2110 | -** called NAME, or a list of the top-level directory if NAME is | |
| 2111 | -** omitted. | |
| 2103 | +** identified by HASH. The /whatis page shows only a description | |
| 2104 | +** of how the artifact is used. The /file page shows the most recent | |
| 2105 | +** version of the file or directory called NAME, or a list of the | |
| 2106 | +** top-level directory if NAME is omitted. | |
| 2107 | +** | |
| 2108 | +** For /artifact and /whatis, the name= query parameter can refer to | |
| 2109 | +** either the name of a file, or an artifact hash. If the ci= query | |
| 2110 | +** parameter is also present, then name= must refer to a file name. | |
| 2111 | +** If ci= is omitted, then the hash interpretation is preferred but | |
| 2112 | +** if name= cannot be understood as a hash, a default "tip" value is | |
| 2113 | +** used for ci=. | |
| 2114 | +** | |
| 2115 | +** For /file, name= can only be interpreted as a filename. As before, | |
| 2116 | +** a default value of "tip" is used for ci= if ci= is omitted. | |
| 2112 | 2117 | */ |
| 2113 | 2118 | void artifact_page(void){ |
| 2114 | 2119 | int rid = 0; |
| 2115 | 2120 | Blob content; |
| 2116 | 2121 | const char *zMime; |
| @@ -2125,143 +2130,138 @@ | ||
| 2125 | 2130 | int isFile = fossil_strcmp(g.zPath,"file")==0; |
| 2126 | 2131 | const char *zLn = P("ln"); |
| 2127 | 2132 | const char *zName = P("name"); |
| 2128 | 2133 | const char *zCI = P("ci"); |
| 2129 | 2134 | HQuery url; |
| 2130 | - Blob dirname; | |
| 2131 | - char *zCIUuid = 0; | |
| 2132 | - int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */ | |
| 2133 | - char *zHeader = 0; | |
| 2134 | - | |
| 2135 | - login_check_credentials(); | |
| 2136 | - if( !g.perm.Read ){ login_needed(g.anon.Read); return; } | |
| 2137 | - url_initialize(&url, g.zPath); | |
| 2138 | - if( zName==0 ){ | |
| 2139 | - zName = P("filename"); | |
| 2140 | - if( zName==0 ) zName = P("fn"); | |
| 2141 | - } | |
| 2142 | - if( zCI && strlen(zCI)==0 ){ zCI = 0; } | |
| 2143 | - if( zCI && zName ){ | |
| 2144 | - blob_zero(&dirname); | |
| 2145 | - hyperlinked_path(zName, &dirname, zCI, "dir", ""); | |
| 2146 | - blob_reset(&dirname); | |
| 2147 | - | |
| 2148 | - if( name_to_uuid2(zCI, "ci", &zCIUuid) ){ | |
| 2149 | - isSymbolicCI = (sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI)) != 0); | |
| 2150 | - } | |
| 2151 | - } | |
| 2152 | - if( isFile && zName ) { | |
| 2153 | - rid = artifact_from_ci_and_filename(0, "name"); | |
| 2154 | - }else{ | |
| 2155 | - rid = artifact_from_ci_and_filename(&url, 0); | |
| 2156 | - } | |
| 2157 | - if( rid==0 ){ | |
| 2158 | - url_add_parameter(&url, "name", zName); | |
| 2159 | - if( isFile ){ | |
| 2160 | - int isUnknownAtCI = 0; | |
| 2161 | - | |
| 2162 | - /* Do a top-level directory listing in /file mode if no argument | |
| 2163 | - ** specified */ | |
| 2164 | - if( zName==0 || zName[0]==0 ){ | |
| 2165 | - if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); | |
| 2166 | - page_tree(); | |
| 2167 | - return; | |
| 2168 | - } | |
| 2169 | - /* Look for a single file with the given name */ | |
| 2170 | - rid = db_int(0, | |
| 2171 | - "SELECT fid FROM filename, mlink, event" | |
| 2172 | - " WHERE name=%Q" | |
| 2173 | - " AND mlink.fnid=filename.fnid" | |
| 2174 | - " AND event.objid=mlink.mid" | |
| 2175 | - " ORDER BY event.mtime DESC LIMIT 1", | |
| 2176 | - zName | |
| 2177 | - ); | |
| 2178 | - /* If found only by the name, then the file is unknown in the check-in. | |
| 2179 | - ** Possibly, the file was renamed/deleted. | |
| 2180 | - */ | |
| 2181 | - if( rid && zCIUuid ){ | |
| 2182 | - rid = 0; | |
| 2183 | - isUnknownAtCI = 1; | |
| 2184 | - } | |
| 2185 | - /* If no file called NAME exists, instead look for a directory | |
| 2186 | - ** with that name, and do a directory listing */ | |
| 2187 | - if( rid==0 ){ | |
| 2188 | - int nName = (int)strlen(zName); | |
| 2189 | - if( nName && zName[nName-1]=='/' ) nName--; | |
| 2190 | - if( db_exists( | |
| 2191 | - "SELECT 1 FROM filename" | |
| 2192 | - " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';", | |
| 2193 | - nName, zName, nName+1, nName, zName | |
| 2194 | - ) ){ | |
| 2195 | - if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); | |
| 2196 | - page_tree(); | |
| 2197 | - return; | |
| 2198 | - } | |
| 2199 | - } | |
| 2200 | - /* If no file or directory called NAME: issue an error */ | |
| 2201 | - if( rid==0 ){ | |
| 2202 | - if( isUnknownAtCI ){ | |
| 2203 | - if( isSymbolicCI ){ | |
| 2204 | - zHeader = mprintf("No such file at %s", zCI); | |
| 2205 | - }else{ | |
| 2206 | - zHeader = mprintf("No such file at [%S]", zCIUuid); | |
| 2207 | - } | |
| 2208 | - style_header("%s", zHeader); | |
| 2209 | - fossil_free(zHeader); | |
| 2210 | - @ File %z(href("%R/finfo?name=%T",zName))%h(zName)</a> is not known | |
| 2211 | - @ at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>]. | |
| 2212 | - }else{ | |
| 2213 | - style_header("No such file"); | |
| 2214 | - @ File '%h(zName)' is not known in this repository. | |
| 2215 | - } | |
| 2216 | - style_footer(); | |
| 2217 | - return; | |
| 2218 | - } | |
| 2219 | - }else{ | |
| 2220 | - rid = name_to_rid_www("name"); | |
| 2221 | - } | |
| 2222 | - } | |
| 2223 | - | |
| 2224 | - if( rid==0 ){ | |
| 2225 | - style_header("No such artifact"); | |
| 2226 | - @ Artifact '%h(zName)' does not exist in this repository. | |
| 2227 | - style_footer(); | |
| 2228 | - return; | |
| 2229 | - } | |
| 2135 | + char *zCIUuid = 0; | |
| 2136 | + int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */ | |
| 2137 | + int isBranchCI = 0; /* ci= refers to a branch name */ | |
| 2138 | + char *zHeader = 0; | |
| 2139 | + | |
| 2140 | + login_check_credentials(); | |
| 2141 | + if( !g.perm.Read ){ login_needed(g.anon.Read); return; } | |
| 2142 | + | |
| 2143 | + /* Capture and normalize the name= and ci= query parameters */ | |
| 2144 | + if( zName==0 ){ | |
| 2145 | + zName = P("filename"); | |
| 2146 | + if( zName==0 ){ | |
| 2147 | + zName = P("fn"); | |
| 2148 | + } | |
| 2149 | + } | |
| 2150 | + if( zCI && strlen(zCI)==0 ){ zCI = 0; } | |
| 2151 | + if( zCI | |
| 2152 | + && name_to_uuid2(zCI, "ci", &zCIUuid) | |
| 2153 | + && sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI))!=0 | |
| 2154 | + ){ | |
| 2155 | + isSymbolicCI = 1; | |
| 2156 | + isBranchCI = branch_includes_uuid(zCI, zCIUuid); | |
| 2157 | + } | |
| 2158 | + | |
| 2159 | + /* The name= query parameter (or at least one of its alternative | |
| 2160 | + ** spellings) is required. Except for /file, show a top-level | |
| 2161 | + ** directory listing if name= is omitted. | |
| 2162 | + */ | |
| 2163 | + if( zName==0 ){ | |
| 2164 | + if( isFile ){ | |
| 2165 | + if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); | |
| 2166 | + page_tree(); | |
| 2167 | + return; | |
| 2168 | + } | |
| 2169 | + style_header("Missing name= query parameter"); | |
| 2170 | + @ The name= query parameter is missing | |
| 2171 | + style_footer(); | |
| 2172 | + return; | |
| 2173 | + } | |
| 2174 | + | |
| 2175 | + url_initialize(&url, g.zPath); | |
| 2176 | + url_add_parameter(&url, "name", zName); | |
| 2177 | + url_add_parameter(&url, "ci", zCI); | |
| 2178 | + | |
| 2179 | + if( zCI==0 && !isFile ){ | |
| 2180 | + /* If there is no ci= query parameter, then prefer to interpret | |
| 2181 | + ** name= as a hash for /artifact and /whatis. But for not for /file. | |
| 2182 | + ** For /file, a name= without a ci= while prefer to use the default | |
| 2183 | + ** "tip" value for ci=. */ | |
| 2184 | + rid = name_to_rid(zName); | |
| 2185 | + } | |
| 2186 | + if( rid==0 ){ | |
| 2187 | + rid = artifact_from_ci_and_filename(0); | |
| 2188 | + } | |
| 2189 | + | |
| 2190 | + if( rid==0 ){ /* Artifact not found */ | |
| 2191 | + if( isFile ){ | |
| 2192 | + /* For /file, also check to see if name= refers to a directory, | |
| 2193 | + ** and if so, do a listing for that directory */ | |
| 2194 | + int nName = (int)strlen(zName); | |
| 2195 | + if( nName && zName[nName-1]=='/' ) nName--; | |
| 2196 | + if( db_exists( | |
| 2197 | + "SELECT 1 FROM filename" | |
| 2198 | + " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';", | |
| 2199 | + nName, zName, nName+1, nName, zName | |
| 2200 | + ) ){ | |
| 2201 | + if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); | |
| 2202 | + page_tree(); | |
| 2203 | + return; | |
| 2204 | + } | |
| 2205 | + style_header("No such file"); | |
| 2206 | + @ File '%h(zName)' does not exist in this repository. | |
| 2207 | + }else{ | |
| 2208 | + style_header("No such artifact"); | |
| 2209 | + @ Artifact '%h(zName)' does not exist in this repository. | |
| 2210 | + } | |
| 2211 | + style_footer(); | |
| 2212 | + return; | |
| 2213 | + } | |
| 2214 | + | |
| 2230 | 2215 | if( descOnly || P("verbose")!=0 ){ |
| 2231 | 2216 | url_add_parameter(&url, "verbose", "1"); |
| 2232 | 2217 | objdescFlags |= OBJDESC_DETAIL; |
| 2233 | 2218 | } |
| 2234 | 2219 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 2235 | 2220 | |
| 2221 | + asText = P("txt")!=0; | |
| 2236 | 2222 | if( isFile ){ |
| 2237 | - if( zCI ){ | |
| 2223 | + if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){ | |
| 2224 | + zCI = "tip"; | |
| 2225 | + @ <h2>File %z(href("%R/finfo?name=%T&m=tip",zName))%h(zName)</a> | |
| 2226 | + @ from the %z(href("%R/info/tip"))latest check-in</a></h2> | |
| 2227 | + }else{ | |
| 2238 | 2228 | const char *zPath; |
| 2239 | 2229 | Blob path; |
| 2240 | 2230 | blob_zero(&path); |
| 2241 | - hyperlinked_path(zName, &path, zCI, "dir", ""); | |
| 2231 | + hyperlinked_path(zName, &path, zCI, "dir", "", LINKPATH_FINFO); | |
| 2242 | 2232 | zPath = blob_str(&path); |
| 2243 | - @ <h2>File %s(zPath) at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>] | |
| 2244 | - @ </h2> | |
| 2233 | + @ <h2>File %s(zPath) \ | |
| 2234 | + if( isBranchCI ){ | |
| 2235 | + @ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2> | |
| 2236 | + }else if( isSymbolicCI ){ | |
| 2237 | + @ as of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2> | |
| 2238 | + }else{ | |
| 2239 | + @ as of check-in [%z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a>]</h2> | |
| 2240 | + } | |
| 2245 | 2241 | blob_reset(&path); |
| 2246 | - }else{ | |
| 2247 | - @ <h2>Latest version of file '%h(zName)':</h2> | |
| 2248 | 2242 | } |
| 2249 | 2243 | style_submenu_element("Artifact", "%R/artifact/%S", zUuid); |
| 2244 | + style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T", | |
| 2245 | + zName, zCI); | |
| 2246 | + style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T", | |
| 2247 | + zName, zCI); | |
| 2248 | + blob_init(&downloadName, zName, -1); | |
| 2249 | + objType = OBJTYPE_CONTENT; | |
| 2250 | 2250 | }else{ |
| 2251 | 2251 | @ <h2>Artifact |
| 2252 | 2252 | style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid); |
| 2253 | 2253 | if( g.perm.Setup ){ |
| 2254 | 2254 | @ (%d(rid)):</h2> |
| 2255 | 2255 | }else{ |
| 2256 | 2256 | @ :</h2> |
| 2257 | 2257 | } |
| 2258 | + blob_zero(&downloadName); | |
| 2259 | + if( asText ) objdescFlags &= ~OBJDESC_BASE; | |
| 2260 | + objType = object_description(rid, objdescFlags, | |
| 2261 | + (isFile?zName:0), &downloadName); | |
| 2258 | 2262 | } |
| 2259 | - blob_zero(&downloadName); | |
| 2260 | - asText = P("txt")!=0; | |
| 2261 | - if( asText ) objdescFlags &= ~OBJDESC_BASE; | |
| 2262 | - objType = object_description(rid, objdescFlags, (isFile ? zName : 0),&downloadName); | |
| 2263 | 2263 | if( !descOnly && P("download")!=0 ){ |
| 2264 | 2264 | cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName), |
| 2265 | 2265 | db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid)); |
| 2266 | 2266 | /*NOTREACHED*/ |
| 2267 | 2267 | } |
| @@ -2289,11 +2289,11 @@ | ||
| 2289 | 2289 | zHeader = mprintf("Artifact [%S]", zUuid); |
| 2290 | 2290 | } |
| 2291 | 2291 | style_header("%s", zHeader); |
| 2292 | 2292 | fossil_free(zCIUuid); |
| 2293 | 2293 | fossil_free(zHeader); |
| 2294 | - if( g.perm.Admin ){ | |
| 2294 | + if( !isFile && g.perm.Admin ){ | |
| 2295 | 2295 | Stmt q; |
| 2296 | 2296 | db_prepare(&q, |
| 2297 | 2297 | "SELECT coalesce(user.login,rcvfrom.uid)," |
| 2298 | 2298 | " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr" |
| 2299 | 2299 | " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid" |
| @@ -2769,15 +2769,15 @@ | ||
| 2769 | 2769 | ** |
| 2770 | 2770 | ** Edit a check-in. (Check-ins are immutable and do not really change. |
| 2771 | 2771 | ** This page really creates supplemental tags that affect the display |
| 2772 | 2772 | ** of the check-in.) |
| 2773 | 2773 | ** |
| 2774 | -** Query parmeters: | |
| 2774 | +** Query parameters: | |
| 2775 | 2775 | ** |
| 2776 | 2776 | ** rid=INTEGER Record ID of the check-in to edit (REQUIRED) |
| 2777 | 2777 | ** |
| 2778 | -** POST parameters after pressing "Perview", "Cancel", or "Apply": | |
| 2778 | +** POST parameters after pressing "Preview", "Cancel", or "Apply": | |
| 2779 | 2779 | ** |
| 2780 | 2780 | ** c=TEXT New check-in comment |
| 2781 | 2781 | ** u=TEXT New user name |
| 2782 | 2782 | ** newclr Apply a background color |
| 2783 | 2783 | ** clr=TEXT New background color (only if newclr) |
| 2784 | 2784 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -666,10 +666,11 @@ | |
| 666 | const char *zOrigDate; |
| 667 | int okWiki = 0; |
| 668 | Blob wiki_read_links = BLOB_INITIALIZER; |
| 669 | Blob wiki_add_links = BLOB_INITIALIZER; |
| 670 | |
| 671 | style_header("Check-in [%S]", zUuid); |
| 672 | login_anonymous_available(); |
| 673 | zEUser = db_text(0, |
| 674 | "SELECT value FROM tagxref" |
| 675 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| @@ -1661,12 +1662,12 @@ | |
| 1661 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1662 | cookie_link_parameter("diff","diff","2"); |
| 1663 | diffType = atoi(PD("diff","2")); |
| 1664 | cookie_render(); |
| 1665 | if( P("from") && P("to") ){ |
| 1666 | v1 = artifact_from_ci_and_filename(0, "from"); |
| 1667 | v2 = artifact_from_ci_and_filename(0, "to"); |
| 1668 | }else{ |
| 1669 | Stmt q; |
| 1670 | v1 = name_to_rid_www("v1"); |
| 1671 | v2 = name_to_rid_www("v2"); |
| 1672 | |
| @@ -1763,12 +1764,12 @@ | |
| 1763 | */ |
| 1764 | void rawartifact_page(void){ |
| 1765 | int rid = 0; |
| 1766 | char *zUuid; |
| 1767 | |
| 1768 | if( P("ci") && P("filename") ){ |
| 1769 | rid = artifact_from_ci_and_filename(0, 0); |
| 1770 | } |
| 1771 | if( rid==0 ){ |
| 1772 | rid = name_to_rid_www("name"); |
| 1773 | } |
| 1774 | login_check_credentials(); |
| @@ -1942,59 +1943,53 @@ | |
| 1942 | |
| 1943 | /* |
| 1944 | ** Look for "ci" and "filename" query parameters. If found, try to |
| 1945 | ** use them to extract the record ID of an artifact for the file. |
| 1946 | ** |
| 1947 | ** Also look for "fn" as an alias for "filename". If either "filename" |
| 1948 | ** or "fn" is present but "ci" is missing, use "tip" as a default value |
| 1949 | ** for "ci". |
| 1950 | ** |
| 1951 | ** If zNameParam is not NULL, this use that parameter as the filename |
| 1952 | ** rather than "fn" or "filename". |
| 1953 | ** |
| 1954 | ** If pUrl is not NULL, then record the "ci" and "filename" values in |
| 1955 | ** pUrl. |
| 1956 | ** |
| 1957 | ** At least one of pUrl or zNameParam must be NULL. |
| 1958 | */ |
| 1959 | int artifact_from_ci_and_filename(HQuery *pUrl, const char *zNameParam){ |
| 1960 | const char *zFilename; |
| 1961 | const char *zCI; |
| 1962 | int cirid; |
| 1963 | Manifest *pManifest; |
| 1964 | ManifestFile *pFile; |
| 1965 | |
| 1966 | if( zNameParam ){ |
| 1967 | zFilename = P(zNameParam); |
| 1968 | }else{ |
| 1969 | zFilename = P("filename"); |
| 1970 | if( zFilename==0 ){ |
| 1971 | zFilename = P("fn"); |
| 1972 | } |
| 1973 | } |
| 1974 | if( zFilename==0 ) return 0; |
| 1975 | |
| 1976 | zCI = P("ci"); |
| 1977 | cirid = name_to_typed_rid(zCI ? zCI : "tip", "ci"); |
| 1978 | if( cirid<=0 ) return 0; |
| 1979 | pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0); |
| 1980 | if( pManifest==0 ) return 0; |
| 1981 | manifest_file_rewind(pManifest); |
| 1982 | while( (pFile = manifest_file_next(pManifest,0))!=0 ){ |
| 1983 | if( fossil_strcmp(zFilename, pFile->zName)==0 ){ |
| 1984 | int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); |
| 1985 | manifest_destroy(pManifest); |
| 1986 | if( pUrl ){ |
| 1987 | assert( zNameParam==0 ); |
| 1988 | url_add_parameter(pUrl, "fn", zFilename); |
| 1989 | if( zCI ) url_add_parameter(pUrl, "ci", zCI); |
| 1990 | } |
| 1991 | return rid; |
| 1992 | } |
| 1993 | } |
| 1994 | manifest_destroy(pManifest); |
| 1995 | return 0; |
| 1996 | } |
| 1997 | |
| 1998 | /* |
| 1999 | ** The "z" argument is a string that contains the text of a source code |
| 2000 | ** file. This routine appends that text to the HTTP reply with line numbering. |
| @@ -2096,21 +2091,31 @@ | |
| 2096 | ** ln=N - highlight line number N |
| 2097 | ** ln=M-N - highlight lines M through N inclusive |
| 2098 | ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive) |
| 2099 | ** verbose - show more detail in the description |
| 2100 | ** download - redirect to the download (artifact page only) |
| 2101 | ** name=NAME - Provide filename or hash as a query parameter |
| 2102 | ** filename=NAME - alternative spelling for "name=" |
| 2103 | ** fn=NAME - alternative spelling for "name=" |
| 2104 | ** ci=VERSION - The specific check-in to use for "filename=". |
| 2105 | ** |
| 2106 | ** The /artifact page show the complete content of a file |
| 2107 | ** identified by HASH as preformatted text. The |
| 2108 | ** /whatis page shows only a description of the file. The /file |
| 2109 | ** page shows the most recent version of the file or directory |
| 2110 | ** called NAME, or a list of the top-level directory if NAME is |
| 2111 | ** omitted. |
| 2112 | */ |
| 2113 | void artifact_page(void){ |
| 2114 | int rid = 0; |
| 2115 | Blob content; |
| 2116 | const char *zMime; |
| @@ -2125,143 +2130,138 @@ | |
| 2125 | int isFile = fossil_strcmp(g.zPath,"file")==0; |
| 2126 | const char *zLn = P("ln"); |
| 2127 | const char *zName = P("name"); |
| 2128 | const char *zCI = P("ci"); |
| 2129 | HQuery url; |
| 2130 | Blob dirname; |
| 2131 | char *zCIUuid = 0; |
| 2132 | int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */ |
| 2133 | char *zHeader = 0; |
| 2134 | |
| 2135 | login_check_credentials(); |
| 2136 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 2137 | url_initialize(&url, g.zPath); |
| 2138 | if( zName==0 ){ |
| 2139 | zName = P("filename"); |
| 2140 | if( zName==0 ) zName = P("fn"); |
| 2141 | } |
| 2142 | if( zCI && strlen(zCI)==0 ){ zCI = 0; } |
| 2143 | if( zCI && zName ){ |
| 2144 | blob_zero(&dirname); |
| 2145 | hyperlinked_path(zName, &dirname, zCI, "dir", ""); |
| 2146 | blob_reset(&dirname); |
| 2147 | |
| 2148 | if( name_to_uuid2(zCI, "ci", &zCIUuid) ){ |
| 2149 | isSymbolicCI = (sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI)) != 0); |
| 2150 | } |
| 2151 | } |
| 2152 | if( isFile && zName ) { |
| 2153 | rid = artifact_from_ci_and_filename(0, "name"); |
| 2154 | }else{ |
| 2155 | rid = artifact_from_ci_and_filename(&url, 0); |
| 2156 | } |
| 2157 | if( rid==0 ){ |
| 2158 | url_add_parameter(&url, "name", zName); |
| 2159 | if( isFile ){ |
| 2160 | int isUnknownAtCI = 0; |
| 2161 | |
| 2162 | /* Do a top-level directory listing in /file mode if no argument |
| 2163 | ** specified */ |
| 2164 | if( zName==0 || zName[0]==0 ){ |
| 2165 | if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); |
| 2166 | page_tree(); |
| 2167 | return; |
| 2168 | } |
| 2169 | /* Look for a single file with the given name */ |
| 2170 | rid = db_int(0, |
| 2171 | "SELECT fid FROM filename, mlink, event" |
| 2172 | " WHERE name=%Q" |
| 2173 | " AND mlink.fnid=filename.fnid" |
| 2174 | " AND event.objid=mlink.mid" |
| 2175 | " ORDER BY event.mtime DESC LIMIT 1", |
| 2176 | zName |
| 2177 | ); |
| 2178 | /* If found only by the name, then the file is unknown in the check-in. |
| 2179 | ** Possibly, the file was renamed/deleted. |
| 2180 | */ |
| 2181 | if( rid && zCIUuid ){ |
| 2182 | rid = 0; |
| 2183 | isUnknownAtCI = 1; |
| 2184 | } |
| 2185 | /* If no file called NAME exists, instead look for a directory |
| 2186 | ** with that name, and do a directory listing */ |
| 2187 | if( rid==0 ){ |
| 2188 | int nName = (int)strlen(zName); |
| 2189 | if( nName && zName[nName-1]=='/' ) nName--; |
| 2190 | if( db_exists( |
| 2191 | "SELECT 1 FROM filename" |
| 2192 | " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';", |
| 2193 | nName, zName, nName+1, nName, zName |
| 2194 | ) ){ |
| 2195 | if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); |
| 2196 | page_tree(); |
| 2197 | return; |
| 2198 | } |
| 2199 | } |
| 2200 | /* If no file or directory called NAME: issue an error */ |
| 2201 | if( rid==0 ){ |
| 2202 | if( isUnknownAtCI ){ |
| 2203 | if( isSymbolicCI ){ |
| 2204 | zHeader = mprintf("No such file at %s", zCI); |
| 2205 | }else{ |
| 2206 | zHeader = mprintf("No such file at [%S]", zCIUuid); |
| 2207 | } |
| 2208 | style_header("%s", zHeader); |
| 2209 | fossil_free(zHeader); |
| 2210 | @ File %z(href("%R/finfo?name=%T",zName))%h(zName)</a> is not known |
| 2211 | @ at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>]. |
| 2212 | }else{ |
| 2213 | style_header("No such file"); |
| 2214 | @ File '%h(zName)' is not known in this repository. |
| 2215 | } |
| 2216 | style_footer(); |
| 2217 | return; |
| 2218 | } |
| 2219 | }else{ |
| 2220 | rid = name_to_rid_www("name"); |
| 2221 | } |
| 2222 | } |
| 2223 | |
| 2224 | if( rid==0 ){ |
| 2225 | style_header("No such artifact"); |
| 2226 | @ Artifact '%h(zName)' does not exist in this repository. |
| 2227 | style_footer(); |
| 2228 | return; |
| 2229 | } |
| 2230 | if( descOnly || P("verbose")!=0 ){ |
| 2231 | url_add_parameter(&url, "verbose", "1"); |
| 2232 | objdescFlags |= OBJDESC_DETAIL; |
| 2233 | } |
| 2234 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 2235 | |
| 2236 | if( isFile ){ |
| 2237 | if( zCI ){ |
| 2238 | const char *zPath; |
| 2239 | Blob path; |
| 2240 | blob_zero(&path); |
| 2241 | hyperlinked_path(zName, &path, zCI, "dir", ""); |
| 2242 | zPath = blob_str(&path); |
| 2243 | @ <h2>File %s(zPath) at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>] |
| 2244 | @ </h2> |
| 2245 | blob_reset(&path); |
| 2246 | }else{ |
| 2247 | @ <h2>Latest version of file '%h(zName)':</h2> |
| 2248 | } |
| 2249 | style_submenu_element("Artifact", "%R/artifact/%S", zUuid); |
| 2250 | }else{ |
| 2251 | @ <h2>Artifact |
| 2252 | style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid); |
| 2253 | if( g.perm.Setup ){ |
| 2254 | @ (%d(rid)):</h2> |
| 2255 | }else{ |
| 2256 | @ :</h2> |
| 2257 | } |
| 2258 | } |
| 2259 | blob_zero(&downloadName); |
| 2260 | asText = P("txt")!=0; |
| 2261 | if( asText ) objdescFlags &= ~OBJDESC_BASE; |
| 2262 | objType = object_description(rid, objdescFlags, (isFile ? zName : 0),&downloadName); |
| 2263 | if( !descOnly && P("download")!=0 ){ |
| 2264 | cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName), |
| 2265 | db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid)); |
| 2266 | /*NOTREACHED*/ |
| 2267 | } |
| @@ -2289,11 +2289,11 @@ | |
| 2289 | zHeader = mprintf("Artifact [%S]", zUuid); |
| 2290 | } |
| 2291 | style_header("%s", zHeader); |
| 2292 | fossil_free(zCIUuid); |
| 2293 | fossil_free(zHeader); |
| 2294 | if( g.perm.Admin ){ |
| 2295 | Stmt q; |
| 2296 | db_prepare(&q, |
| 2297 | "SELECT coalesce(user.login,rcvfrom.uid)," |
| 2298 | " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr" |
| 2299 | " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid" |
| @@ -2769,15 +2769,15 @@ | |
| 2769 | ** |
| 2770 | ** Edit a check-in. (Check-ins are immutable and do not really change. |
| 2771 | ** This page really creates supplemental tags that affect the display |
| 2772 | ** of the check-in.) |
| 2773 | ** |
| 2774 | ** Query parmeters: |
| 2775 | ** |
| 2776 | ** rid=INTEGER Record ID of the check-in to edit (REQUIRED) |
| 2777 | ** |
| 2778 | ** POST parameters after pressing "Perview", "Cancel", or "Apply": |
| 2779 | ** |
| 2780 | ** c=TEXT New check-in comment |
| 2781 | ** u=TEXT New user name |
| 2782 | ** newclr Apply a background color |
| 2783 | ** clr=TEXT New background color (only if newclr) |
| 2784 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -666,10 +666,11 @@ | |
| 666 | const char *zOrigDate; |
| 667 | int okWiki = 0; |
| 668 | Blob wiki_read_links = BLOB_INITIALIZER; |
| 669 | Blob wiki_add_links = BLOB_INITIALIZER; |
| 670 | |
| 671 | Th_Store("current_checkin", zName); |
| 672 | style_header("Check-in [%S]", zUuid); |
| 673 | login_anonymous_available(); |
| 674 | zEUser = db_text(0, |
| 675 | "SELECT value FROM tagxref" |
| 676 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| @@ -1661,12 +1662,12 @@ | |
| 1662 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1663 | cookie_link_parameter("diff","diff","2"); |
| 1664 | diffType = atoi(PD("diff","2")); |
| 1665 | cookie_render(); |
| 1666 | if( P("from") && P("to") ){ |
| 1667 | v1 = artifact_from_ci_and_filename("from"); |
| 1668 | v2 = artifact_from_ci_and_filename("to"); |
| 1669 | }else{ |
| 1670 | Stmt q; |
| 1671 | v1 = name_to_rid_www("v1"); |
| 1672 | v2 = name_to_rid_www("v2"); |
| 1673 | |
| @@ -1763,12 +1764,12 @@ | |
| 1764 | */ |
| 1765 | void rawartifact_page(void){ |
| 1766 | int rid = 0; |
| 1767 | char *zUuid; |
| 1768 | |
| 1769 | if( P("ci") ){ |
| 1770 | rid = artifact_from_ci_and_filename(0); |
| 1771 | } |
| 1772 | if( rid==0 ){ |
| 1773 | rid = name_to_rid_www("name"); |
| 1774 | } |
| 1775 | login_check_credentials(); |
| @@ -1942,59 +1943,53 @@ | |
| 1943 | |
| 1944 | /* |
| 1945 | ** Look for "ci" and "filename" query parameters. If found, try to |
| 1946 | ** use them to extract the record ID of an artifact for the file. |
| 1947 | ** |
| 1948 | ** Also look for "fn" and "name" as an aliases for "filename". If any |
| 1949 | ** "filename" or "fn" or "name" are present but "ci" is missing, then |
| 1950 | ** use "tip" as the default value for "ci". |
| 1951 | ** |
| 1952 | ** If zNameParam is not NULL, then use that parameter as the filename |
| 1953 | ** rather than "fn" or "filename" or "name". the zNameParam is used |
| 1954 | ** for the from= and to= query parameters of /fdiff. |
| 1955 | */ |
| 1956 | int artifact_from_ci_and_filename(const char *zNameParam){ |
| 1957 | const char *zFilename; |
| 1958 | const char *zCI; |
| 1959 | int cirid; |
| 1960 | Manifest *pManifest; |
| 1961 | ManifestFile *pFile; |
| 1962 | int rid = 0; |
| 1963 | |
| 1964 | if( zNameParam ){ |
| 1965 | zFilename = P(zNameParam); |
| 1966 | }else{ |
| 1967 | zFilename = P("filename"); |
| 1968 | if( zFilename==0 ){ |
| 1969 | zFilename = P("fn"); |
| 1970 | } |
| 1971 | if( zFilename==0 ){ |
| 1972 | zFilename = P("name"); |
| 1973 | } |
| 1974 | } |
| 1975 | if( zFilename==0 ) return 0; |
| 1976 | |
| 1977 | zCI = PD("ci", "tip"); |
| 1978 | cirid = name_to_typed_rid(zCI, "ci"); |
| 1979 | if( cirid<=0 ) return 0; |
| 1980 | pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0); |
| 1981 | if( pManifest==0 ) return 0; |
| 1982 | manifest_file_rewind(pManifest); |
| 1983 | while( (pFile = manifest_file_next(pManifest,0))!=0 ){ |
| 1984 | if( fossil_strcmp(zFilename, pFile->zName)==0 ){ |
| 1985 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); |
| 1986 | break; |
| 1987 | } |
| 1988 | } |
| 1989 | manifest_destroy(pManifest); |
| 1990 | return rid; |
| 1991 | } |
| 1992 | |
| 1993 | /* |
| 1994 | ** The "z" argument is a string that contains the text of a source code |
| 1995 | ** file. This routine appends that text to the HTTP reply with line numbering. |
| @@ -2096,21 +2091,31 @@ | |
| 2091 | ** ln=N - highlight line number N |
| 2092 | ** ln=M-N - highlight lines M through N inclusive |
| 2093 | ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive) |
| 2094 | ** verbose - show more detail in the description |
| 2095 | ** download - redirect to the download (artifact page only) |
| 2096 | ** name=NAME - filename or hash as a query parameter |
| 2097 | ** filename=NAME - alternative spelling for "name=" |
| 2098 | ** fn=NAME - alternative spelling for "name=" |
| 2099 | ** ci=VERSION - The specific check-in to use with "name=" to |
| 2100 | ** identify the file. |
| 2101 | ** |
| 2102 | ** The /artifact page show the complete content of a file |
| 2103 | ** identified by HASH. The /whatis page shows only a description |
| 2104 | ** of how the artifact is used. The /file page shows the most recent |
| 2105 | ** version of the file or directory called NAME, or a list of the |
| 2106 | ** top-level directory if NAME is omitted. |
| 2107 | ** |
| 2108 | ** For /artifact and /whatis, the name= query parameter can refer to |
| 2109 | ** either the name of a file, or an artifact hash. If the ci= query |
| 2110 | ** parameter is also present, then name= must refer to a file name. |
| 2111 | ** If ci= is omitted, then the hash interpretation is preferred but |
| 2112 | ** if name= cannot be understood as a hash, a default "tip" value is |
| 2113 | ** used for ci=. |
| 2114 | ** |
| 2115 | ** For /file, name= can only be interpreted as a filename. As before, |
| 2116 | ** a default value of "tip" is used for ci= if ci= is omitted. |
| 2117 | */ |
| 2118 | void artifact_page(void){ |
| 2119 | int rid = 0; |
| 2120 | Blob content; |
| 2121 | const char *zMime; |
| @@ -2125,143 +2130,138 @@ | |
| 2130 | int isFile = fossil_strcmp(g.zPath,"file")==0; |
| 2131 | const char *zLn = P("ln"); |
| 2132 | const char *zName = P("name"); |
| 2133 | const char *zCI = P("ci"); |
| 2134 | HQuery url; |
| 2135 | char *zCIUuid = 0; |
| 2136 | int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */ |
| 2137 | int isBranchCI = 0; /* ci= refers to a branch name */ |
| 2138 | char *zHeader = 0; |
| 2139 | |
| 2140 | login_check_credentials(); |
| 2141 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 2142 | |
| 2143 | /* Capture and normalize the name= and ci= query parameters */ |
| 2144 | if( zName==0 ){ |
| 2145 | zName = P("filename"); |
| 2146 | if( zName==0 ){ |
| 2147 | zName = P("fn"); |
| 2148 | } |
| 2149 | } |
| 2150 | if( zCI && strlen(zCI)==0 ){ zCI = 0; } |
| 2151 | if( zCI |
| 2152 | && name_to_uuid2(zCI, "ci", &zCIUuid) |
| 2153 | && sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI))!=0 |
| 2154 | ){ |
| 2155 | isSymbolicCI = 1; |
| 2156 | isBranchCI = branch_includes_uuid(zCI, zCIUuid); |
| 2157 | } |
| 2158 | |
| 2159 | /* The name= query parameter (or at least one of its alternative |
| 2160 | ** spellings) is required. Except for /file, show a top-level |
| 2161 | ** directory listing if name= is omitted. |
| 2162 | */ |
| 2163 | if( zName==0 ){ |
| 2164 | if( isFile ){ |
| 2165 | if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); |
| 2166 | page_tree(); |
| 2167 | return; |
| 2168 | } |
| 2169 | style_header("Missing name= query parameter"); |
| 2170 | @ The name= query parameter is missing |
| 2171 | style_footer(); |
| 2172 | return; |
| 2173 | } |
| 2174 | |
| 2175 | url_initialize(&url, g.zPath); |
| 2176 | url_add_parameter(&url, "name", zName); |
| 2177 | url_add_parameter(&url, "ci", zCI); |
| 2178 | |
| 2179 | if( zCI==0 && !isFile ){ |
| 2180 | /* If there is no ci= query parameter, then prefer to interpret |
| 2181 | ** name= as a hash for /artifact and /whatis. But for not for /file. |
| 2182 | ** For /file, a name= without a ci= while prefer to use the default |
| 2183 | ** "tip" value for ci=. */ |
| 2184 | rid = name_to_rid(zName); |
| 2185 | } |
| 2186 | if( rid==0 ){ |
| 2187 | rid = artifact_from_ci_and_filename(0); |
| 2188 | } |
| 2189 | |
| 2190 | if( rid==0 ){ /* Artifact not found */ |
| 2191 | if( isFile ){ |
| 2192 | /* For /file, also check to see if name= refers to a directory, |
| 2193 | ** and if so, do a listing for that directory */ |
| 2194 | int nName = (int)strlen(zName); |
| 2195 | if( nName && zName[nName-1]=='/' ) nName--; |
| 2196 | if( db_exists( |
| 2197 | "SELECT 1 FROM filename" |
| 2198 | " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';", |
| 2199 | nName, zName, nName+1, nName, zName |
| 2200 | ) ){ |
| 2201 | if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); |
| 2202 | page_tree(); |
| 2203 | return; |
| 2204 | } |
| 2205 | style_header("No such file"); |
| 2206 | @ File '%h(zName)' does not exist in this repository. |
| 2207 | }else{ |
| 2208 | style_header("No such artifact"); |
| 2209 | @ Artifact '%h(zName)' does not exist in this repository. |
| 2210 | } |
| 2211 | style_footer(); |
| 2212 | return; |
| 2213 | } |
| 2214 | |
| 2215 | if( descOnly || P("verbose")!=0 ){ |
| 2216 | url_add_parameter(&url, "verbose", "1"); |
| 2217 | objdescFlags |= OBJDESC_DETAIL; |
| 2218 | } |
| 2219 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 2220 | |
| 2221 | asText = P("txt")!=0; |
| 2222 | if( isFile ){ |
| 2223 | if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){ |
| 2224 | zCI = "tip"; |
| 2225 | @ <h2>File %z(href("%R/finfo?name=%T&m=tip",zName))%h(zName)</a> |
| 2226 | @ from the %z(href("%R/info/tip"))latest check-in</a></h2> |
| 2227 | }else{ |
| 2228 | const char *zPath; |
| 2229 | Blob path; |
| 2230 | blob_zero(&path); |
| 2231 | hyperlinked_path(zName, &path, zCI, "dir", "", LINKPATH_FINFO); |
| 2232 | zPath = blob_str(&path); |
| 2233 | @ <h2>File %s(zPath) \ |
| 2234 | if( isBranchCI ){ |
| 2235 | @ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2> |
| 2236 | }else if( isSymbolicCI ){ |
| 2237 | @ as of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2> |
| 2238 | }else{ |
| 2239 | @ as of check-in [%z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a>]</h2> |
| 2240 | } |
| 2241 | blob_reset(&path); |
| 2242 | } |
| 2243 | style_submenu_element("Artifact", "%R/artifact/%S", zUuid); |
| 2244 | style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T", |
| 2245 | zName, zCI); |
| 2246 | style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T", |
| 2247 | zName, zCI); |
| 2248 | blob_init(&downloadName, zName, -1); |
| 2249 | objType = OBJTYPE_CONTENT; |
| 2250 | }else{ |
| 2251 | @ <h2>Artifact |
| 2252 | style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid); |
| 2253 | if( g.perm.Setup ){ |
| 2254 | @ (%d(rid)):</h2> |
| 2255 | }else{ |
| 2256 | @ :</h2> |
| 2257 | } |
| 2258 | blob_zero(&downloadName); |
| 2259 | if( asText ) objdescFlags &= ~OBJDESC_BASE; |
| 2260 | objType = object_description(rid, objdescFlags, |
| 2261 | (isFile?zName:0), &downloadName); |
| 2262 | } |
| 2263 | if( !descOnly && P("download")!=0 ){ |
| 2264 | cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName), |
| 2265 | db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid)); |
| 2266 | /*NOTREACHED*/ |
| 2267 | } |
| @@ -2289,11 +2289,11 @@ | |
| 2289 | zHeader = mprintf("Artifact [%S]", zUuid); |
| 2290 | } |
| 2291 | style_header("%s", zHeader); |
| 2292 | fossil_free(zCIUuid); |
| 2293 | fossil_free(zHeader); |
| 2294 | if( !isFile && g.perm.Admin ){ |
| 2295 | Stmt q; |
| 2296 | db_prepare(&q, |
| 2297 | "SELECT coalesce(user.login,rcvfrom.uid)," |
| 2298 | " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr" |
| 2299 | " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid" |
| @@ -2769,15 +2769,15 @@ | |
| 2769 | ** |
| 2770 | ** Edit a check-in. (Check-ins are immutable and do not really change. |
| 2771 | ** This page really creates supplemental tags that affect the display |
| 2772 | ** of the check-in.) |
| 2773 | ** |
| 2774 | ** Query parameters: |
| 2775 | ** |
| 2776 | ** rid=INTEGER Record ID of the check-in to edit (REQUIRED) |
| 2777 | ** |
| 2778 | ** POST parameters after pressing "Preview", "Cancel", or "Apply": |
| 2779 | ** |
| 2780 | ** c=TEXT New check-in comment |
| 2781 | ** u=TEXT New user name |
| 2782 | ** newclr Apply a background color |
| 2783 | ** clr=TEXT New background color (only if newclr) |
| 2784 |
+34
-28
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -678,20 +678,18 @@ | ||
| 678 | 678 | }else{ |
| 679 | 679 | zAnonPw = 0; |
| 680 | 680 | } |
| 681 | 681 | @ <table class="login_out"> |
| 682 | 682 | @ <tr> |
| 683 | - @ <td class="form_label">User ID:</td> | |
| 684 | - if( anonFlag ){ | |
| 685 | - @ <td><input type="text" id="u" name="u" value="anonymous" size="30"></td> | |
| 686 | - }else{ | |
| 687 | - @ <td><input type="text" id="u" name="u" value="" size="30" /></td> | |
| 688 | - } | |
| 683 | + @ <td class="form_label" id="userlabel1">User ID:</td> | |
| 684 | + @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \ | |
| 685 | + @ size="30" value="%s(anonFlag?"anonymous":"")"></td> | |
| 689 | 686 | @ </tr> |
| 690 | 687 | @ <tr> |
| 691 | - @ <td class="form_label">Password:</td> | |
| 692 | - @ <td><input type="password" id="p" name="p" value="" size="30" />\ | |
| 688 | + @ <td class="form_label" id="pswdlabel">Password:</td> | |
| 689 | + @ <td><input aria-labelledby="pswdlabel" type="password" id="p" \ | |
| 690 | + @ name="p" value="" size="30" />\ | |
| 693 | 691 | if( zAnonPw && !noAnon ){ |
| 694 | 692 | captcha_speakit_button(uSeed, "Speak password for \"anonymous\""); |
| 695 | 693 | } |
| 696 | 694 | @ </td> |
| 697 | 695 | @ </tr> |
| @@ -750,16 +748,19 @@ | ||
| 750 | 748 | if( g.perm.Password ){ |
| 751 | 749 | @ <hr> |
| 752 | 750 | @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p> |
| 753 | 751 | form_begin(0, "%R/login"); |
| 754 | 752 | @ <table> |
| 755 | - @ <tr><td class="form_label">Old Password:</td> | |
| 756 | - @ <td><input type="password" name="p" size="30" /></td></tr> | |
| 757 | - @ <tr><td class="form_label">New Password:</td> | |
| 758 | - @ <td><input type="password" name="n1" size="30" /></td></tr> | |
| 759 | - @ <tr><td class="form_label">Repeat New Password:</td> | |
| 760 | - @ <td><input type="password" name="n2" size="30" /></td></tr> | |
| 753 | + @ <tr><td class="form_label" id="oldpw">Old Password:</td> | |
| 754 | + @ <td><input aria-labelledby="oldpw" type="password" name="p" \ | |
| 755 | + @ size="30"/></td></tr> | |
| 756 | + @ <tr><td class="form_label" id="newpw">New Password:</td> | |
| 757 | + @ <td><input aria-labelledby="newpw" type="password" name="n1" \ | |
| 758 | + @ size="30" /></td></tr> | |
| 759 | + @ <tr><td class="form_label" id="reppw">Repeat New Password:</td> | |
| 760 | + @ <td><input aria-labledby="reppw" type="password" name="n2" \ | |
| 761 | + @ size="30" /></td></tr> | |
| 761 | 762 | @ <tr><td></td> |
| 762 | 763 | @ <td><input type="submit" value="Change Password" /></td></tr> |
| 763 | 764 | @ </table> |
| 764 | 765 | @ </form> |
| 765 | 766 | } |
| @@ -1690,57 +1691,62 @@ | ||
| 1690 | 1691 | @ <input type="hidden" name="g" value="%h(P("g"))" /> |
| 1691 | 1692 | } |
| 1692 | 1693 | @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)" /> |
| 1693 | 1694 | @ <table class="login_out"> |
| 1694 | 1695 | @ <tr> |
| 1695 | - @ <td class="form_label" align="right">User ID:</td> | |
| 1696 | - @ <td><input type="text" name="u" value="%h(zUserID)" size="30"></td> | |
| 1696 | + @ <td class="form_label" align="right" id="uid">User ID:</td> | |
| 1697 | + @ <td><input aria-labelledby="uid" type="text" name="u" \ | |
| 1698 | + @ value="%h(zUserID)" size="30"></td> | |
| 1697 | 1699 | @ |
| 1698 | 1700 | if( iErrLine==1 ){ |
| 1699 | 1701 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1700 | 1702 | } |
| 1701 | 1703 | @ <tr> |
| 1702 | - @ <td class="form_label" align="right">Display Name:</td> | |
| 1703 | - @ <td><input type="text" name="dn" value="%h(zDName)" size="30"></td> | |
| 1704 | + @ <td class="form_label" align="right" id="dpyname">Display Name:</td> | |
| 1705 | + @ <td><input aria-labelledby="dpyname" type="text" name="dn" \ | |
| 1706 | + @ value="%h(zDName)" size="30"></td> | |
| 1704 | 1707 | @ </tr> |
| 1705 | 1708 | if( iErrLine==2 ){ |
| 1706 | 1709 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1707 | 1710 | } |
| 1708 | 1711 | @ </tr> |
| 1709 | 1712 | @ <tr> |
| 1710 | - @ <td class="form_label" align="right">Email Address:</td> | |
| 1711 | - @ <td><input type="text" name="ea" value="%h(zEAddr)" size="30"></td> | |
| 1713 | + @ <td class="form_label" align="right" id="emaddr">Email Address:</td> | |
| 1714 | + @ <td><input aria-labelledby="emaddr" type="text" name="ea" \ | |
| 1715 | + @ value="%h(zEAddr)" size="30"></td> | |
| 1712 | 1716 | @ </tr> |
| 1713 | 1717 | if( iErrLine==3 ){ |
| 1714 | 1718 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1715 | 1719 | } |
| 1716 | 1720 | if( canDoAlerts ){ |
| 1717 | 1721 | int a = atoi(PD("alerts","1")); |
| 1718 | 1722 | @ <tr> |
| 1719 | - @ <td class="form_label" align="right">Email Alerts?</td> | |
| 1720 | - @ <td><select size='1' name='alerts'> | |
| 1723 | + @ <td class="form_label" align="right" id="emalrt">Email Alerts?</td> | |
| 1724 | + @ <td><select aria-labelledby="emalrt" size='1' name='alerts'> | |
| 1721 | 1725 | @ <option value="1" %s(a?"selected":"")>Yes</option> |
| 1722 | 1726 | @ <option value="0" %s(!a?"selected":"")>No</option> |
| 1723 | 1727 | @ </select></td></tr> |
| 1724 | 1728 | } |
| 1725 | 1729 | @ <tr> |
| 1726 | - @ <td class="form_label" align="right">Password:</td> | |
| 1727 | - @ <td><input type="password" name="p" value="%h(zPasswd)" size="30"></td> | |
| 1730 | + @ <td class="form_label" align="right" id="pswd">Password:</td> | |
| 1731 | + @ <td><input aria-labelledby="pswd" type="password" name="p" \ | |
| 1732 | + @ value="%h(zPasswd)" size="30"></td> | |
| 1728 | 1733 | @ <tr> |
| 1729 | 1734 | if( iErrLine==4 ){ |
| 1730 | 1735 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1731 | 1736 | } |
| 1732 | 1737 | @ <tr> |
| 1733 | - @ <td class="form_label" align="right">Confirm:</td> | |
| 1734 | - @ <td><input type="password" name="cp" value="%h(zConfirm)" size="30"></td> | |
| 1738 | + @ <td class="form_label" align="right" id="pwcfrm">Confirm:</td> | |
| 1739 | + @ <td><input aria-labelledby="pwcfrm" type="password" name="cp" \ | |
| 1740 | + @ value="%h(zConfirm)" size="30"></td> | |
| 1735 | 1741 | @ </tr> |
| 1736 | 1742 | if( iErrLine==5 ){ |
| 1737 | 1743 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1738 | 1744 | } |
| 1739 | 1745 | @ <tr> |
| 1740 | - @ <td class="form_label" align="right">Captcha:</td> | |
| 1741 | - @ <td><input type="text" name="captcha" \ | |
| 1746 | + @ <td class="form_label" align="right" id="cptcha">Captcha:</td> | |
| 1747 | + @ <td><input type="text" name="captcha" aria-labelledby="cptcha" \ | |
| 1742 | 1748 | @ value="%h(captchaIsCorrect?zDecoded:"")" size="30"> |
| 1743 | 1749 | captcha_speakit_button(uSeed, "Speak the captcha text"); |
| 1744 | 1750 | @ </td> |
| 1745 | 1751 | @ </tr> |
| 1746 | 1752 | if( iErrLine==6 ){ |
| 1747 | 1753 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -678,20 +678,18 @@ | |
| 678 | }else{ |
| 679 | zAnonPw = 0; |
| 680 | } |
| 681 | @ <table class="login_out"> |
| 682 | @ <tr> |
| 683 | @ <td class="form_label">User ID:</td> |
| 684 | if( anonFlag ){ |
| 685 | @ <td><input type="text" id="u" name="u" value="anonymous" size="30"></td> |
| 686 | }else{ |
| 687 | @ <td><input type="text" id="u" name="u" value="" size="30" /></td> |
| 688 | } |
| 689 | @ </tr> |
| 690 | @ <tr> |
| 691 | @ <td class="form_label">Password:</td> |
| 692 | @ <td><input type="password" id="p" name="p" value="" size="30" />\ |
| 693 | if( zAnonPw && !noAnon ){ |
| 694 | captcha_speakit_button(uSeed, "Speak password for \"anonymous\""); |
| 695 | } |
| 696 | @ </td> |
| 697 | @ </tr> |
| @@ -750,16 +748,19 @@ | |
| 750 | if( g.perm.Password ){ |
| 751 | @ <hr> |
| 752 | @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p> |
| 753 | form_begin(0, "%R/login"); |
| 754 | @ <table> |
| 755 | @ <tr><td class="form_label">Old Password:</td> |
| 756 | @ <td><input type="password" name="p" size="30" /></td></tr> |
| 757 | @ <tr><td class="form_label">New Password:</td> |
| 758 | @ <td><input type="password" name="n1" size="30" /></td></tr> |
| 759 | @ <tr><td class="form_label">Repeat New Password:</td> |
| 760 | @ <td><input type="password" name="n2" size="30" /></td></tr> |
| 761 | @ <tr><td></td> |
| 762 | @ <td><input type="submit" value="Change Password" /></td></tr> |
| 763 | @ </table> |
| 764 | @ </form> |
| 765 | } |
| @@ -1690,57 +1691,62 @@ | |
| 1690 | @ <input type="hidden" name="g" value="%h(P("g"))" /> |
| 1691 | } |
| 1692 | @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)" /> |
| 1693 | @ <table class="login_out"> |
| 1694 | @ <tr> |
| 1695 | @ <td class="form_label" align="right">User ID:</td> |
| 1696 | @ <td><input type="text" name="u" value="%h(zUserID)" size="30"></td> |
| 1697 | @ |
| 1698 | if( iErrLine==1 ){ |
| 1699 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1700 | } |
| 1701 | @ <tr> |
| 1702 | @ <td class="form_label" align="right">Display Name:</td> |
| 1703 | @ <td><input type="text" name="dn" value="%h(zDName)" size="30"></td> |
| 1704 | @ </tr> |
| 1705 | if( iErrLine==2 ){ |
| 1706 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1707 | } |
| 1708 | @ </tr> |
| 1709 | @ <tr> |
| 1710 | @ <td class="form_label" align="right">Email Address:</td> |
| 1711 | @ <td><input type="text" name="ea" value="%h(zEAddr)" size="30"></td> |
| 1712 | @ </tr> |
| 1713 | if( iErrLine==3 ){ |
| 1714 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1715 | } |
| 1716 | if( canDoAlerts ){ |
| 1717 | int a = atoi(PD("alerts","1")); |
| 1718 | @ <tr> |
| 1719 | @ <td class="form_label" align="right">Email Alerts?</td> |
| 1720 | @ <td><select size='1' name='alerts'> |
| 1721 | @ <option value="1" %s(a?"selected":"")>Yes</option> |
| 1722 | @ <option value="0" %s(!a?"selected":"")>No</option> |
| 1723 | @ </select></td></tr> |
| 1724 | } |
| 1725 | @ <tr> |
| 1726 | @ <td class="form_label" align="right">Password:</td> |
| 1727 | @ <td><input type="password" name="p" value="%h(zPasswd)" size="30"></td> |
| 1728 | @ <tr> |
| 1729 | if( iErrLine==4 ){ |
| 1730 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1731 | } |
| 1732 | @ <tr> |
| 1733 | @ <td class="form_label" align="right">Confirm:</td> |
| 1734 | @ <td><input type="password" name="cp" value="%h(zConfirm)" size="30"></td> |
| 1735 | @ </tr> |
| 1736 | if( iErrLine==5 ){ |
| 1737 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1738 | } |
| 1739 | @ <tr> |
| 1740 | @ <td class="form_label" align="right">Captcha:</td> |
| 1741 | @ <td><input type="text" name="captcha" \ |
| 1742 | @ value="%h(captchaIsCorrect?zDecoded:"")" size="30"> |
| 1743 | captcha_speakit_button(uSeed, "Speak the captcha text"); |
| 1744 | @ </td> |
| 1745 | @ </tr> |
| 1746 | if( iErrLine==6 ){ |
| 1747 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -678,20 +678,18 @@ | |
| 678 | }else{ |
| 679 | zAnonPw = 0; |
| 680 | } |
| 681 | @ <table class="login_out"> |
| 682 | @ <tr> |
| 683 | @ <td class="form_label" id="userlabel1">User ID:</td> |
| 684 | @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \ |
| 685 | @ size="30" value="%s(anonFlag?"anonymous":"")"></td> |
| 686 | @ </tr> |
| 687 | @ <tr> |
| 688 | @ <td class="form_label" id="pswdlabel">Password:</td> |
| 689 | @ <td><input aria-labelledby="pswdlabel" type="password" id="p" \ |
| 690 | @ name="p" value="" size="30" />\ |
| 691 | if( zAnonPw && !noAnon ){ |
| 692 | captcha_speakit_button(uSeed, "Speak password for \"anonymous\""); |
| 693 | } |
| 694 | @ </td> |
| 695 | @ </tr> |
| @@ -750,16 +748,19 @@ | |
| 748 | if( g.perm.Password ){ |
| 749 | @ <hr> |
| 750 | @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p> |
| 751 | form_begin(0, "%R/login"); |
| 752 | @ <table> |
| 753 | @ <tr><td class="form_label" id="oldpw">Old Password:</td> |
| 754 | @ <td><input aria-labelledby="oldpw" type="password" name="p" \ |
| 755 | @ size="30"/></td></tr> |
| 756 | @ <tr><td class="form_label" id="newpw">New Password:</td> |
| 757 | @ <td><input aria-labelledby="newpw" type="password" name="n1" \ |
| 758 | @ size="30" /></td></tr> |
| 759 | @ <tr><td class="form_label" id="reppw">Repeat New Password:</td> |
| 760 | @ <td><input aria-labledby="reppw" type="password" name="n2" \ |
| 761 | @ size="30" /></td></tr> |
| 762 | @ <tr><td></td> |
| 763 | @ <td><input type="submit" value="Change Password" /></td></tr> |
| 764 | @ </table> |
| 765 | @ </form> |
| 766 | } |
| @@ -1690,57 +1691,62 @@ | |
| 1691 | @ <input type="hidden" name="g" value="%h(P("g"))" /> |
| 1692 | } |
| 1693 | @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)" /> |
| 1694 | @ <table class="login_out"> |
| 1695 | @ <tr> |
| 1696 | @ <td class="form_label" align="right" id="uid">User ID:</td> |
| 1697 | @ <td><input aria-labelledby="uid" type="text" name="u" \ |
| 1698 | @ value="%h(zUserID)" size="30"></td> |
| 1699 | @ |
| 1700 | if( iErrLine==1 ){ |
| 1701 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1702 | } |
| 1703 | @ <tr> |
| 1704 | @ <td class="form_label" align="right" id="dpyname">Display Name:</td> |
| 1705 | @ <td><input aria-labelledby="dpyname" type="text" name="dn" \ |
| 1706 | @ value="%h(zDName)" size="30"></td> |
| 1707 | @ </tr> |
| 1708 | if( iErrLine==2 ){ |
| 1709 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1710 | } |
| 1711 | @ </tr> |
| 1712 | @ <tr> |
| 1713 | @ <td class="form_label" align="right" id="emaddr">Email Address:</td> |
| 1714 | @ <td><input aria-labelledby="emaddr" type="text" name="ea" \ |
| 1715 | @ value="%h(zEAddr)" size="30"></td> |
| 1716 | @ </tr> |
| 1717 | if( iErrLine==3 ){ |
| 1718 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1719 | } |
| 1720 | if( canDoAlerts ){ |
| 1721 | int a = atoi(PD("alerts","1")); |
| 1722 | @ <tr> |
| 1723 | @ <td class="form_label" align="right" id="emalrt">Email Alerts?</td> |
| 1724 | @ <td><select aria-labelledby="emalrt" size='1' name='alerts'> |
| 1725 | @ <option value="1" %s(a?"selected":"")>Yes</option> |
| 1726 | @ <option value="0" %s(!a?"selected":"")>No</option> |
| 1727 | @ </select></td></tr> |
| 1728 | } |
| 1729 | @ <tr> |
| 1730 | @ <td class="form_label" align="right" id="pswd">Password:</td> |
| 1731 | @ <td><input aria-labelledby="pswd" type="password" name="p" \ |
| 1732 | @ value="%h(zPasswd)" size="30"></td> |
| 1733 | @ <tr> |
| 1734 | if( iErrLine==4 ){ |
| 1735 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1736 | } |
| 1737 | @ <tr> |
| 1738 | @ <td class="form_label" align="right" id="pwcfrm">Confirm:</td> |
| 1739 | @ <td><input aria-labelledby="pwcfrm" type="password" name="cp" \ |
| 1740 | @ value="%h(zConfirm)" size="30"></td> |
| 1741 | @ </tr> |
| 1742 | if( iErrLine==5 ){ |
| 1743 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1744 | } |
| 1745 | @ <tr> |
| 1746 | @ <td class="form_label" align="right" id="cptcha">Captcha:</td> |
| 1747 | @ <td><input type="text" name="captcha" aria-labelledby="cptcha" \ |
| 1748 | @ value="%h(captchaIsCorrect?zDecoded:"")" size="30"> |
| 1749 | captcha_speakit_button(uSeed, "Speak the captcha text"); |
| 1750 | @ </td> |
| 1751 | @ </tr> |
| 1752 | if( iErrLine==6 ){ |
| 1753 |
+3
-3
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -34,11 +34,11 @@ | ||
| 34 | 34 | ** The following macros help determine those lengths. FOSSIL_HASH_DIGITS |
| 35 | 35 | ** is the default number of digits to display to humans. This value can |
| 36 | 36 | ** be overridden using the hash-digits setting. FOSSIL_HASH_DIGITS_URL |
| 37 | 37 | ** is the minimum number of digits to be used in URLs. The number used |
| 38 | 38 | ** will always be at least 6 more than the number used for human output, |
| 39 | -** or 40 if the number of digits in human output is 34 or more. | |
| 39 | +** or HNAME_MAX, whichever is least. | |
| 40 | 40 | */ |
| 41 | 41 | #ifndef FOSSIL_HASH_DIGITS |
| 42 | 42 | # define FOSSIL_HASH_DIGITS 10 /* For %S (human display) */ |
| 43 | 43 | #endif |
| 44 | 44 | #ifndef FOSSIL_HASH_DIGITS_URL |
| @@ -54,14 +54,14 @@ | ||
| 54 | 54 | static int nDigitHuman = 0; |
| 55 | 55 | static int nDigitUrl = 0; |
| 56 | 56 | if( nDigitHuman==0 ){ |
| 57 | 57 | nDigitHuman = db_get_int("hash-digits", FOSSIL_HASH_DIGITS); |
| 58 | 58 | if( nDigitHuman < 6 ) nDigitHuman = 6; |
| 59 | - if( nDigitHuman > 40 ) nDigitHuman = 40; | |
| 59 | + if( nDigitHuman > HNAME_MAX ) nDigitHuman = HNAME_MAX; | |
| 60 | 60 | nDigitUrl = nDigitHuman + 6; |
| 61 | 61 | if( nDigitUrl < FOSSIL_HASH_DIGITS_URL ) nDigitUrl = FOSSIL_HASH_DIGITS_URL; |
| 62 | - if( nDigitUrl > 40 ) nDigitUrl = 40; | |
| 62 | + if( nDigitUrl > HNAME_MAX ) nDigitUrl = HNAME_MAX; | |
| 63 | 63 | } |
| 64 | 64 | return bForUrl ? nDigitUrl : nDigitHuman; |
| 65 | 65 | } |
| 66 | 66 | |
| 67 | 67 | /* |
| 68 | 68 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -34,11 +34,11 @@ | |
| 34 | ** The following macros help determine those lengths. FOSSIL_HASH_DIGITS |
| 35 | ** is the default number of digits to display to humans. This value can |
| 36 | ** be overridden using the hash-digits setting. FOSSIL_HASH_DIGITS_URL |
| 37 | ** is the minimum number of digits to be used in URLs. The number used |
| 38 | ** will always be at least 6 more than the number used for human output, |
| 39 | ** or 40 if the number of digits in human output is 34 or more. |
| 40 | */ |
| 41 | #ifndef FOSSIL_HASH_DIGITS |
| 42 | # define FOSSIL_HASH_DIGITS 10 /* For %S (human display) */ |
| 43 | #endif |
| 44 | #ifndef FOSSIL_HASH_DIGITS_URL |
| @@ -54,14 +54,14 @@ | |
| 54 | static int nDigitHuman = 0; |
| 55 | static int nDigitUrl = 0; |
| 56 | if( nDigitHuman==0 ){ |
| 57 | nDigitHuman = db_get_int("hash-digits", FOSSIL_HASH_DIGITS); |
| 58 | if( nDigitHuman < 6 ) nDigitHuman = 6; |
| 59 | if( nDigitHuman > 40 ) nDigitHuman = 40; |
| 60 | nDigitUrl = nDigitHuman + 6; |
| 61 | if( nDigitUrl < FOSSIL_HASH_DIGITS_URL ) nDigitUrl = FOSSIL_HASH_DIGITS_URL; |
| 62 | if( nDigitUrl > 40 ) nDigitUrl = 40; |
| 63 | } |
| 64 | return bForUrl ? nDigitUrl : nDigitHuman; |
| 65 | } |
| 66 | |
| 67 | /* |
| 68 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -34,11 +34,11 @@ | |
| 34 | ** The following macros help determine those lengths. FOSSIL_HASH_DIGITS |
| 35 | ** is the default number of digits to display to humans. This value can |
| 36 | ** be overridden using the hash-digits setting. FOSSIL_HASH_DIGITS_URL |
| 37 | ** is the minimum number of digits to be used in URLs. The number used |
| 38 | ** will always be at least 6 more than the number used for human output, |
| 39 | ** or HNAME_MAX, whichever is least. |
| 40 | */ |
| 41 | #ifndef FOSSIL_HASH_DIGITS |
| 42 | # define FOSSIL_HASH_DIGITS 10 /* For %S (human display) */ |
| 43 | #endif |
| 44 | #ifndef FOSSIL_HASH_DIGITS_URL |
| @@ -54,14 +54,14 @@ | |
| 54 | static int nDigitHuman = 0; |
| 55 | static int nDigitUrl = 0; |
| 56 | if( nDigitHuman==0 ){ |
| 57 | nDigitHuman = db_get_int("hash-digits", FOSSIL_HASH_DIGITS); |
| 58 | if( nDigitHuman < 6 ) nDigitHuman = 6; |
| 59 | if( nDigitHuman > HNAME_MAX ) nDigitHuman = HNAME_MAX; |
| 60 | nDigitUrl = nDigitHuman + 6; |
| 61 | if( nDigitUrl < FOSSIL_HASH_DIGITS_URL ) nDigitUrl = FOSSIL_HASH_DIGITS_URL; |
| 62 | if( nDigitUrl > HNAME_MAX ) nDigitUrl = HNAME_MAX; |
| 63 | } |
| 64 | return bForUrl ? nDigitUrl : nDigitHuman; |
| 65 | } |
| 66 | |
| 67 | /* |
| 68 |
+32
-25
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -201,16 +201,17 @@ | ||
| 201 | 201 | admin_log("Set option [%q] to [%q].", |
| 202 | 202 | zVar, iQ ? "on" : "off"); |
| 203 | 203 | iVal = iQ; |
| 204 | 204 | } |
| 205 | 205 | } |
| 206 | - @ <label><input type="checkbox" name="%s(zQParm)" | |
| 206 | + @ <label><input type="checkbox" name="%s(zQParm)" \ | |
| 207 | + @ aria-label="%s(zLabel[0]?zLabel:zQParm)" \ | |
| 207 | 208 | if( iVal ){ |
| 208 | - @ checked="checked" | |
| 209 | + @ checked="checked" \ | |
| 209 | 210 | } |
| 210 | 211 | if( disabled ){ |
| 211 | - @ disabled="disabled" | |
| 212 | + @ disabled="disabled" \ | |
| 212 | 213 | } |
| 213 | 214 | @ /> <b>%s(zLabel)</b></label> |
| 214 | 215 | } |
| 215 | 216 | |
| 216 | 217 | /* |
| @@ -232,12 +233,12 @@ | ||
| 232 | 233 | db_set(zVar, zQ, 0); |
| 233 | 234 | admin_log("Set entry_attribute %Q to: %.*s%s", |
| 234 | 235 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 235 | 236 | zVal = zQ; |
| 236 | 237 | } |
| 237 | - @ <input type="text" id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" \ | |
| 238 | - @ size="%d(width)" \ | |
| 238 | + @ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \ | |
| 239 | + @ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \ | |
| 239 | 240 | if( disabled ){ |
| 240 | 241 | @ disabled="disabled" \ |
| 241 | 242 | } |
| 242 | 243 | @ /> <b>%s(zLabel)</b> |
| 243 | 244 | } |
| @@ -263,16 +264,17 @@ | ||
| 263 | 264 | admin_log("Set textarea_attribute %Q to: %.*s%s", |
| 264 | 265 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 265 | 266 | z = zQ; |
| 266 | 267 | } |
| 267 | 268 | if( rows>0 && cols>0 ){ |
| 268 | - @ <textarea id="id%s(zQP)" name="%s(zQP)" rows="%d(rows)" | |
| 269 | + @ <textarea id="id%s(zQP)" name="%s(zQP)" rows="%d(rows)" \ | |
| 270 | + @ aria-label="%h(zLabel[0]?zLabel:zQP)" \ | |
| 269 | 271 | if( disabled ){ |
| 270 | - @ disabled="disabled" | |
| 272 | + @ disabled="disabled" \ | |
| 271 | 273 | } |
| 272 | 274 | @ cols="%d(cols)">%h(z)</textarea> |
| 273 | - if( zLabel && *zLabel ){ | |
| 275 | + if( *zLabel ){ | |
| 274 | 276 | @ <span class="textareaLabel">%s(zLabel)</span> |
| 275 | 277 | } |
| 276 | 278 | } |
| 277 | 279 | return z; |
| 278 | 280 | } |
| @@ -297,11 +299,11 @@ | ||
| 297 | 299 | db_set(zVar, zQ, 0); |
| 298 | 300 | admin_log("Set multiple_choice_attribute %Q to: %.*s%s", |
| 299 | 301 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 300 | 302 | z = zQ; |
| 301 | 303 | } |
| 302 | - @ <select size="1" name="%s(zQP)" id="id%s(zQP)"> | |
| 304 | + @ <select aria-label="%h(zLabel)" size="1" name="%s(zQP)" id="id%s(zQP)"> | |
| 303 | 305 | for(i=0; i<nChoice*2; i+=2){ |
| 304 | 306 | const char *zSel = fossil_strcmp(azChoice[i],z)==0 ? " selected" : ""; |
| 305 | 307 | @ <option value="%h(azChoice[i])"%s(zSel)>%h(azChoice[i+1])</option> |
| 306 | 308 | } |
| 307 | 309 | @ </select> <b>%h(zLabel)</b> |
| @@ -398,11 +400,11 @@ | ||
| 398 | 400 | @ <hr /> |
| 399 | 401 | onoff_attribute("Allow HTTP_AUTHENTICATION authentication", |
| 400 | 402 | "http_authentication_ok", "http_authentication_ok", 0, 0); |
| 401 | 403 | @ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment |
| 402 | 404 | @ variable or the "Authentication:" HTTP header to find the username and |
| 403 | - @ password. This is another way of supporting Basic Authenitication. | |
| 405 | + @ password. This is another way of supporting Basic Authentication. | |
| 404 | 406 | @ (Property: "http_authentication_ok") |
| 405 | 407 | @ </p> |
| 406 | 408 | @ |
| 407 | 409 | @ <hr /> |
| 408 | 410 | entry_attribute("Login expiration time", 6, "cookie-expire", "cex", |
| @@ -606,25 +608,30 @@ | ||
| 606 | 608 | @ |
| 607 | 609 | @ <form action="%s(g.zTop)/setup_login_group" method="post"><div> |
| 608 | 610 | login_insert_csrf_secret(); |
| 609 | 611 | @ <blockquote><table border="0"> |
| 610 | 612 | @ |
| 611 | - @ <tr><th align="right">Repository filename in group to join:</th> | |
| 612 | - @ <td width="5"></td><td> | |
| 613 | - @ <input type="text" size="50" value="%h(zRepo)" name="repo"></td></tr> | |
| 614 | - @ | |
| 615 | - @ <tr><th align="right">Login on the above repo:</th> | |
| 616 | - @ <td width="5"></td><td> | |
| 617 | - @ <input type="text" size="20" value="%h(zLogin)" name="login"></td></tr> | |
| 618 | - @ | |
| 619 | - @ <tr><th align="right">Password:</th> | |
| 620 | - @ <td width="5"></td><td> | |
| 621 | - @ <input type="password" size="20" name="pw"></td></tr> | |
| 622 | - @ | |
| 623 | - @ <tr><th align="right">Name of login-group:</th> | |
| 624 | - @ <td width="5"></td><td> | |
| 625 | - @ <input type="text" size="30" value="%h(zNewName)" name="newname"> | |
| 613 | + @ <tr><th align="right" id="rfigtj">Repository filename \ | |
| 614 | + @ in group to join:</th> | |
| 615 | + @ <td width="5"></td><td> | |
| 616 | + @ <input aria-labelledby="rfigtj" type="text" size="50" \ | |
| 617 | + @ value="%h(zRepo)" name="repo"></td></tr> | |
| 618 | + @ | |
| 619 | + @ <tr><th align="right" id="lotar">Login on the above repo:</th> | |
| 620 | + @ <td width="5"></td><td> | |
| 621 | + @ <input aria-labelledby="lotar" type="text" size="20" \ | |
| 622 | + @ value="%h(zLogin)" name="login"></td></tr> | |
| 623 | + @ | |
| 624 | + @ <tr><th align="right" id="lgpw">Password:</th> | |
| 625 | + @ <td width="5"></td><td> | |
| 626 | + @ <input aria-labelledby="lgpw" type="password" size="20" name="pw">\ | |
| 627 | + @ </td></tr> | |
| 628 | + @ | |
| 629 | + @ <tr><th align="right" id="nolg">Name of login-group:</th> | |
| 630 | + @ <td width="5"></td><td> | |
| 631 | + @ <input aria-labelledby="nolg" type="text" size="30" \ | |
| 632 | + @ value="%h(zNewName)" name="newname"> | |
| 626 | 633 | @ (only used if creating a new login-group).</td></tr> |
| 627 | 634 | @ |
| 628 | 635 | @ <tr><td colspan="3" align="center"> |
| 629 | 636 | @ <input type="submit" value="Join" name="join"></td></tr> |
| 630 | 637 | @ </table></blockquote></div></form> |
| 631 | 638 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -201,16 +201,17 @@ | |
| 201 | admin_log("Set option [%q] to [%q].", |
| 202 | zVar, iQ ? "on" : "off"); |
| 203 | iVal = iQ; |
| 204 | } |
| 205 | } |
| 206 | @ <label><input type="checkbox" name="%s(zQParm)" |
| 207 | if( iVal ){ |
| 208 | @ checked="checked" |
| 209 | } |
| 210 | if( disabled ){ |
| 211 | @ disabled="disabled" |
| 212 | } |
| 213 | @ /> <b>%s(zLabel)</b></label> |
| 214 | } |
| 215 | |
| 216 | /* |
| @@ -232,12 +233,12 @@ | |
| 232 | db_set(zVar, zQ, 0); |
| 233 | admin_log("Set entry_attribute %Q to: %.*s%s", |
| 234 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 235 | zVal = zQ; |
| 236 | } |
| 237 | @ <input type="text" id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" \ |
| 238 | @ size="%d(width)" \ |
| 239 | if( disabled ){ |
| 240 | @ disabled="disabled" \ |
| 241 | } |
| 242 | @ /> <b>%s(zLabel)</b> |
| 243 | } |
| @@ -263,16 +264,17 @@ | |
| 263 | admin_log("Set textarea_attribute %Q to: %.*s%s", |
| 264 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 265 | z = zQ; |
| 266 | } |
| 267 | if( rows>0 && cols>0 ){ |
| 268 | @ <textarea id="id%s(zQP)" name="%s(zQP)" rows="%d(rows)" |
| 269 | if( disabled ){ |
| 270 | @ disabled="disabled" |
| 271 | } |
| 272 | @ cols="%d(cols)">%h(z)</textarea> |
| 273 | if( zLabel && *zLabel ){ |
| 274 | @ <span class="textareaLabel">%s(zLabel)</span> |
| 275 | } |
| 276 | } |
| 277 | return z; |
| 278 | } |
| @@ -297,11 +299,11 @@ | |
| 297 | db_set(zVar, zQ, 0); |
| 298 | admin_log("Set multiple_choice_attribute %Q to: %.*s%s", |
| 299 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 300 | z = zQ; |
| 301 | } |
| 302 | @ <select size="1" name="%s(zQP)" id="id%s(zQP)"> |
| 303 | for(i=0; i<nChoice*2; i+=2){ |
| 304 | const char *zSel = fossil_strcmp(azChoice[i],z)==0 ? " selected" : ""; |
| 305 | @ <option value="%h(azChoice[i])"%s(zSel)>%h(azChoice[i+1])</option> |
| 306 | } |
| 307 | @ </select> <b>%h(zLabel)</b> |
| @@ -398,11 +400,11 @@ | |
| 398 | @ <hr /> |
| 399 | onoff_attribute("Allow HTTP_AUTHENTICATION authentication", |
| 400 | "http_authentication_ok", "http_authentication_ok", 0, 0); |
| 401 | @ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment |
| 402 | @ variable or the "Authentication:" HTTP header to find the username and |
| 403 | @ password. This is another way of supporting Basic Authenitication. |
| 404 | @ (Property: "http_authentication_ok") |
| 405 | @ </p> |
| 406 | @ |
| 407 | @ <hr /> |
| 408 | entry_attribute("Login expiration time", 6, "cookie-expire", "cex", |
| @@ -606,25 +608,30 @@ | |
| 606 | @ |
| 607 | @ <form action="%s(g.zTop)/setup_login_group" method="post"><div> |
| 608 | login_insert_csrf_secret(); |
| 609 | @ <blockquote><table border="0"> |
| 610 | @ |
| 611 | @ <tr><th align="right">Repository filename in group to join:</th> |
| 612 | @ <td width="5"></td><td> |
| 613 | @ <input type="text" size="50" value="%h(zRepo)" name="repo"></td></tr> |
| 614 | @ |
| 615 | @ <tr><th align="right">Login on the above repo:</th> |
| 616 | @ <td width="5"></td><td> |
| 617 | @ <input type="text" size="20" value="%h(zLogin)" name="login"></td></tr> |
| 618 | @ |
| 619 | @ <tr><th align="right">Password:</th> |
| 620 | @ <td width="5"></td><td> |
| 621 | @ <input type="password" size="20" name="pw"></td></tr> |
| 622 | @ |
| 623 | @ <tr><th align="right">Name of login-group:</th> |
| 624 | @ <td width="5"></td><td> |
| 625 | @ <input type="text" size="30" value="%h(zNewName)" name="newname"> |
| 626 | @ (only used if creating a new login-group).</td></tr> |
| 627 | @ |
| 628 | @ <tr><td colspan="3" align="center"> |
| 629 | @ <input type="submit" value="Join" name="join"></td></tr> |
| 630 | @ </table></blockquote></div></form> |
| 631 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -201,16 +201,17 @@ | |
| 201 | admin_log("Set option [%q] to [%q].", |
| 202 | zVar, iQ ? "on" : "off"); |
| 203 | iVal = iQ; |
| 204 | } |
| 205 | } |
| 206 | @ <label><input type="checkbox" name="%s(zQParm)" \ |
| 207 | @ aria-label="%s(zLabel[0]?zLabel:zQParm)" \ |
| 208 | if( iVal ){ |
| 209 | @ checked="checked" \ |
| 210 | } |
| 211 | if( disabled ){ |
| 212 | @ disabled="disabled" \ |
| 213 | } |
| 214 | @ /> <b>%s(zLabel)</b></label> |
| 215 | } |
| 216 | |
| 217 | /* |
| @@ -232,12 +233,12 @@ | |
| 233 | db_set(zVar, zQ, 0); |
| 234 | admin_log("Set entry_attribute %Q to: %.*s%s", |
| 235 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 236 | zVal = zQ; |
| 237 | } |
| 238 | @ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \ |
| 239 | @ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \ |
| 240 | if( disabled ){ |
| 241 | @ disabled="disabled" \ |
| 242 | } |
| 243 | @ /> <b>%s(zLabel)</b> |
| 244 | } |
| @@ -263,16 +264,17 @@ | |
| 264 | admin_log("Set textarea_attribute %Q to: %.*s%s", |
| 265 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 266 | z = zQ; |
| 267 | } |
| 268 | if( rows>0 && cols>0 ){ |
| 269 | @ <textarea id="id%s(zQP)" name="%s(zQP)" rows="%d(rows)" \ |
| 270 | @ aria-label="%h(zLabel[0]?zLabel:zQP)" \ |
| 271 | if( disabled ){ |
| 272 | @ disabled="disabled" \ |
| 273 | } |
| 274 | @ cols="%d(cols)">%h(z)</textarea> |
| 275 | if( *zLabel ){ |
| 276 | @ <span class="textareaLabel">%s(zLabel)</span> |
| 277 | } |
| 278 | } |
| 279 | return z; |
| 280 | } |
| @@ -297,11 +299,11 @@ | |
| 299 | db_set(zVar, zQ, 0); |
| 300 | admin_log("Set multiple_choice_attribute %Q to: %.*s%s", |
| 301 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 302 | z = zQ; |
| 303 | } |
| 304 | @ <select aria-label="%h(zLabel)" size="1" name="%s(zQP)" id="id%s(zQP)"> |
| 305 | for(i=0; i<nChoice*2; i+=2){ |
| 306 | const char *zSel = fossil_strcmp(azChoice[i],z)==0 ? " selected" : ""; |
| 307 | @ <option value="%h(azChoice[i])"%s(zSel)>%h(azChoice[i+1])</option> |
| 308 | } |
| 309 | @ </select> <b>%h(zLabel)</b> |
| @@ -398,11 +400,11 @@ | |
| 400 | @ <hr /> |
| 401 | onoff_attribute("Allow HTTP_AUTHENTICATION authentication", |
| 402 | "http_authentication_ok", "http_authentication_ok", 0, 0); |
| 403 | @ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment |
| 404 | @ variable or the "Authentication:" HTTP header to find the username and |
| 405 | @ password. This is another way of supporting Basic Authentication. |
| 406 | @ (Property: "http_authentication_ok") |
| 407 | @ </p> |
| 408 | @ |
| 409 | @ <hr /> |
| 410 | entry_attribute("Login expiration time", 6, "cookie-expire", "cex", |
| @@ -606,25 +608,30 @@ | |
| 608 | @ |
| 609 | @ <form action="%s(g.zTop)/setup_login_group" method="post"><div> |
| 610 | login_insert_csrf_secret(); |
| 611 | @ <blockquote><table border="0"> |
| 612 | @ |
| 613 | @ <tr><th align="right" id="rfigtj">Repository filename \ |
| 614 | @ in group to join:</th> |
| 615 | @ <td width="5"></td><td> |
| 616 | @ <input aria-labelledby="rfigtj" type="text" size="50" \ |
| 617 | @ value="%h(zRepo)" name="repo"></td></tr> |
| 618 | @ |
| 619 | @ <tr><th align="right" id="lotar">Login on the above repo:</th> |
| 620 | @ <td width="5"></td><td> |
| 621 | @ <input aria-labelledby="lotar" type="text" size="20" \ |
| 622 | @ value="%h(zLogin)" name="login"></td></tr> |
| 623 | @ |
| 624 | @ <tr><th align="right" id="lgpw">Password:</th> |
| 625 | @ <td width="5"></td><td> |
| 626 | @ <input aria-labelledby="lgpw" type="password" size="20" name="pw">\ |
| 627 | @ </td></tr> |
| 628 | @ |
| 629 | @ <tr><th align="right" id="nolg">Name of login-group:</th> |
| 630 | @ <td width="5"></td><td> |
| 631 | @ <input aria-labelledby="nolg" type="text" size="30" \ |
| 632 | @ value="%h(zNewName)" name="newname"> |
| 633 | @ (only used if creating a new login-group).</td></tr> |
| 634 | @ |
| 635 | @ <tr><td colspan="3" align="center"> |
| 636 | @ <input type="submit" value="Join" name="join"></td></tr> |
| 637 | @ </table></blockquote></div></form> |
| 638 |
+16
-12
| --- src/setupuser.c | ||
| +++ src/setupuser.c | ||
| @@ -534,24 +534,27 @@ | ||
| 534 | 534 | @ <input type="hidden" name="pw" value="*"> |
| 535 | 535 | } |
| 536 | 536 | @ <input type="hidden" name="referer" value="%h(cgi_referer("setup_ulist"))"> |
| 537 | 537 | @ <table width="100%%"> |
| 538 | 538 | @ <tr> |
| 539 | - @ <td class="usetupEditLabel">User ID:</td> | |
| 539 | + @ <td class="usetupEditLabel" id="suuid">User ID:</td> | |
| 540 | 540 | if( uid ){ |
| 541 | - @ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)" />\ | |
| 541 | + @ <td>%d(uid) <input aria-labelledby="suuid" type="hidden" \ | |
| 542 | + @ name="id" value="%d(uid)"/>\ | |
| 542 | 543 | @ </td> |
| 543 | 544 | }else{ |
| 544 | - @ <td>(new user)<input type="hidden" name="id" value="0" /></td> | |
| 545 | + @ <td>(new user)<input aria-labelledby="suuid" type="hidden" name="id" \ | |
| 546 | + @ value="0" /></td> | |
| 545 | 547 | } |
| 546 | 548 | @ </tr> |
| 547 | 549 | @ <tr> |
| 548 | - @ <td class="usetupEditLabel">Login:</td> | |
| 550 | + @ <td class="usetupEditLabel" id="sulgn">Login:</td> | |
| 549 | 551 | if( login_is_special(zLogin) ){ |
| 550 | 552 | @ <td><b>%h(zLogin)</b></td> |
| 551 | 553 | }else{ |
| 552 | - @ <td><input type="text" name="login" value="%h(zLogin)" />\ | |
| 554 | + @ <td><input aria-labelledby="sulgn" type="text" name="login" \ | |
| 555 | + @ value="%h(zLogin)" /> | |
| 553 | 556 | if( alert_tables_exist() ){ |
| 554 | 557 | int sid; |
| 555 | 558 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 556 | 559 | " WHERE suname=%Q", zLogin); |
| 557 | 560 | if( sid>0 ){ |
| @@ -559,12 +562,13 @@ | ||
| 559 | 562 | @ (subscription info for %h(zLogin))</a>\ |
| 560 | 563 | } |
| 561 | 564 | } |
| 562 | 565 | @ </td></tr> |
| 563 | 566 | @ <tr> |
| 564 | - @ <td class="usetupEditLabel">Contact Info:</td> | |
| 565 | - @ <td><textarea name="info" cols="40" rows="2">%h(zInfo)</textarea></td> | |
| 567 | + @ <td class="usetupEditLabel" id="sucnfo">Contact Info:</td> | |
| 568 | + @ <td><textarea aria-labelledby="sucnfo" name="info" cols="40" \ | |
| 569 | + @ rows="2">%h(zInfo)</textarea></td> | |
| 566 | 570 | } |
| 567 | 571 | @ </tr> |
| 568 | 572 | @ <tr> |
| 569 | 573 | @ <td class="usetupEditLabel">Capabilities:</td> |
| 570 | 574 | @ <td width="100%%"> |
| @@ -653,19 +657,19 @@ | ||
| 653 | 657 | @ <a href="%R/setup_ucap_list">(key)</a> |
| 654 | 658 | @ </td> |
| 655 | 659 | @ </tr> |
| 656 | 660 | if( !login_is_special(zLogin) ){ |
| 657 | 661 | @ <tr> |
| 658 | - @ <td align="right">Password:</td> | |
| 662 | + @ <td align="right" id="supw">Password:</td> | |
| 659 | 663 | if( zPw[0] ){ |
| 660 | 664 | /* Obscure the password for all users */ |
| 661 | - @ <td><input type="password" autocomplete="off" name="pw"\ | |
| 662 | - @ value="**********" /></td> | |
| 665 | + @ <td><input aria-labelledby="supw" type="password" autocomplete="off" \ | |
| 666 | + @ name="pw" value="**********" /></td> | |
| 663 | 667 | }else{ |
| 664 | 668 | /* Show an empty password as an empty input field */ |
| 665 | - @ <td><input type="password" autocomplete="off" name="pw"\ | |
| 666 | - @ value="" /></td> | |
| 669 | + @ <td><input aria-labelledby="supw" type="password" name="pw" \ | |
| 670 | + @ autocomplete="off" value="" /></td> | |
| 667 | 671 | } |
| 668 | 672 | @ </tr> |
| 669 | 673 | } |
| 670 | 674 | zGroup = login_group_name(); |
| 671 | 675 | if( zGroup ){ |
| 672 | 676 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -534,24 +534,27 @@ | |
| 534 | @ <input type="hidden" name="pw" value="*"> |
| 535 | } |
| 536 | @ <input type="hidden" name="referer" value="%h(cgi_referer("setup_ulist"))"> |
| 537 | @ <table width="100%%"> |
| 538 | @ <tr> |
| 539 | @ <td class="usetupEditLabel">User ID:</td> |
| 540 | if( uid ){ |
| 541 | @ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)" />\ |
| 542 | @ </td> |
| 543 | }else{ |
| 544 | @ <td>(new user)<input type="hidden" name="id" value="0" /></td> |
| 545 | } |
| 546 | @ </tr> |
| 547 | @ <tr> |
| 548 | @ <td class="usetupEditLabel">Login:</td> |
| 549 | if( login_is_special(zLogin) ){ |
| 550 | @ <td><b>%h(zLogin)</b></td> |
| 551 | }else{ |
| 552 | @ <td><input type="text" name="login" value="%h(zLogin)" />\ |
| 553 | if( alert_tables_exist() ){ |
| 554 | int sid; |
| 555 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 556 | " WHERE suname=%Q", zLogin); |
| 557 | if( sid>0 ){ |
| @@ -559,12 +562,13 @@ | |
| 559 | @ (subscription info for %h(zLogin))</a>\ |
| 560 | } |
| 561 | } |
| 562 | @ </td></tr> |
| 563 | @ <tr> |
| 564 | @ <td class="usetupEditLabel">Contact Info:</td> |
| 565 | @ <td><textarea name="info" cols="40" rows="2">%h(zInfo)</textarea></td> |
| 566 | } |
| 567 | @ </tr> |
| 568 | @ <tr> |
| 569 | @ <td class="usetupEditLabel">Capabilities:</td> |
| 570 | @ <td width="100%%"> |
| @@ -653,19 +657,19 @@ | |
| 653 | @ <a href="%R/setup_ucap_list">(key)</a> |
| 654 | @ </td> |
| 655 | @ </tr> |
| 656 | if( !login_is_special(zLogin) ){ |
| 657 | @ <tr> |
| 658 | @ <td align="right">Password:</td> |
| 659 | if( zPw[0] ){ |
| 660 | /* Obscure the password for all users */ |
| 661 | @ <td><input type="password" autocomplete="off" name="pw"\ |
| 662 | @ value="**********" /></td> |
| 663 | }else{ |
| 664 | /* Show an empty password as an empty input field */ |
| 665 | @ <td><input type="password" autocomplete="off" name="pw"\ |
| 666 | @ value="" /></td> |
| 667 | } |
| 668 | @ </tr> |
| 669 | } |
| 670 | zGroup = login_group_name(); |
| 671 | if( zGroup ){ |
| 672 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -534,24 +534,27 @@ | |
| 534 | @ <input type="hidden" name="pw" value="*"> |
| 535 | } |
| 536 | @ <input type="hidden" name="referer" value="%h(cgi_referer("setup_ulist"))"> |
| 537 | @ <table width="100%%"> |
| 538 | @ <tr> |
| 539 | @ <td class="usetupEditLabel" id="suuid">User ID:</td> |
| 540 | if( uid ){ |
| 541 | @ <td>%d(uid) <input aria-labelledby="suuid" type="hidden" \ |
| 542 | @ name="id" value="%d(uid)"/>\ |
| 543 | @ </td> |
| 544 | }else{ |
| 545 | @ <td>(new user)<input aria-labelledby="suuid" type="hidden" name="id" \ |
| 546 | @ value="0" /></td> |
| 547 | } |
| 548 | @ </tr> |
| 549 | @ <tr> |
| 550 | @ <td class="usetupEditLabel" id="sulgn">Login:</td> |
| 551 | if( login_is_special(zLogin) ){ |
| 552 | @ <td><b>%h(zLogin)</b></td> |
| 553 | }else{ |
| 554 | @ <td><input aria-labelledby="sulgn" type="text" name="login" \ |
| 555 | @ value="%h(zLogin)" /> |
| 556 | if( alert_tables_exist() ){ |
| 557 | int sid; |
| 558 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 559 | " WHERE suname=%Q", zLogin); |
| 560 | if( sid>0 ){ |
| @@ -559,12 +562,13 @@ | |
| 562 | @ (subscription info for %h(zLogin))</a>\ |
| 563 | } |
| 564 | } |
| 565 | @ </td></tr> |
| 566 | @ <tr> |
| 567 | @ <td class="usetupEditLabel" id="sucnfo">Contact Info:</td> |
| 568 | @ <td><textarea aria-labelledby="sucnfo" name="info" cols="40" \ |
| 569 | @ rows="2">%h(zInfo)</textarea></td> |
| 570 | } |
| 571 | @ </tr> |
| 572 | @ <tr> |
| 573 | @ <td class="usetupEditLabel">Capabilities:</td> |
| 574 | @ <td width="100%%"> |
| @@ -653,19 +657,19 @@ | |
| 657 | @ <a href="%R/setup_ucap_list">(key)</a> |
| 658 | @ </td> |
| 659 | @ </tr> |
| 660 | if( !login_is_special(zLogin) ){ |
| 661 | @ <tr> |
| 662 | @ <td align="right" id="supw">Password:</td> |
| 663 | if( zPw[0] ){ |
| 664 | /* Obscure the password for all users */ |
| 665 | @ <td><input aria-labelledby="supw" type="password" autocomplete="off" \ |
| 666 | @ name="pw" value="**********" /></td> |
| 667 | }else{ |
| 668 | /* Show an empty password as an empty input field */ |
| 669 | @ <td><input aria-labelledby="supw" type="password" name="pw" \ |
| 670 | @ autocomplete="off" value="" /></td> |
| 671 | } |
| 672 | @ </tr> |
| 673 | } |
| 674 | zGroup = login_group_name(); |
| 675 | if( zGroup ){ |
| 676 |
+5
-2
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -7998,13 +7998,16 @@ | ||
| 7998 | 7998 | while( rc==SQLITE_OK && sqlite3_step(pExplain)==SQLITE_ROW ){ |
| 7999 | 7999 | /* int iId = sqlite3_column_int(pExplain, 0); */ |
| 8000 | 8000 | /* int iParent = sqlite3_column_int(pExplain, 1); */ |
| 8001 | 8001 | /* int iNotUsed = sqlite3_column_int(pExplain, 2); */ |
| 8002 | 8002 | const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3); |
| 8003 | - int nDetail = STRLEN(zDetail); | |
| 8003 | + int nDetail; | |
| 8004 | 8004 | int i; |
| 8005 | 8005 | |
| 8006 | + if( !zDetail ) continue; | |
| 8007 | + nDetail = STRLEN(zDetail); | |
| 8008 | + | |
| 8006 | 8009 | for(i=0; i<nDetail; i++){ |
| 8007 | 8010 | const char *zIdx = 0; |
| 8008 | 8011 | if( memcmp(&zDetail[i], " USING INDEX ", 13)==0 ){ |
| 8009 | 8012 | zIdx = &zDetail[i+13]; |
| 8010 | 8013 | }else if( memcmp(&zDetail[i], " USING COVERING INDEX ", 22)==0 ){ |
| @@ -8820,11 +8823,11 @@ | ||
| 8820 | 8823 | sqlite3_free(p->zCandidates); |
| 8821 | 8824 | sqlite3_free(p); |
| 8822 | 8825 | } |
| 8823 | 8826 | } |
| 8824 | 8827 | |
| 8825 | -#endif /* ifndef SQLITE_OMIT_VIRTUAL_TABLE */ | |
| 8828 | +#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ | |
| 8826 | 8829 | |
| 8827 | 8830 | /************************* End ../ext/expert/sqlite3expert.c ********************/ |
| 8828 | 8831 | |
| 8829 | 8832 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 8830 | 8833 | /************************* Begin ../ext/misc/dbdata.c ******************/ |
| 8831 | 8834 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -7998,13 +7998,16 @@ | |
| 7998 | while( rc==SQLITE_OK && sqlite3_step(pExplain)==SQLITE_ROW ){ |
| 7999 | /* int iId = sqlite3_column_int(pExplain, 0); */ |
| 8000 | /* int iParent = sqlite3_column_int(pExplain, 1); */ |
| 8001 | /* int iNotUsed = sqlite3_column_int(pExplain, 2); */ |
| 8002 | const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3); |
| 8003 | int nDetail = STRLEN(zDetail); |
| 8004 | int i; |
| 8005 | |
| 8006 | for(i=0; i<nDetail; i++){ |
| 8007 | const char *zIdx = 0; |
| 8008 | if( memcmp(&zDetail[i], " USING INDEX ", 13)==0 ){ |
| 8009 | zIdx = &zDetail[i+13]; |
| 8010 | }else if( memcmp(&zDetail[i], " USING COVERING INDEX ", 22)==0 ){ |
| @@ -8820,11 +8823,11 @@ | |
| 8820 | sqlite3_free(p->zCandidates); |
| 8821 | sqlite3_free(p); |
| 8822 | } |
| 8823 | } |
| 8824 | |
| 8825 | #endif /* ifndef SQLITE_OMIT_VIRTUAL_TABLE */ |
| 8826 | |
| 8827 | /************************* End ../ext/expert/sqlite3expert.c ********************/ |
| 8828 | |
| 8829 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 8830 | /************************* Begin ../ext/misc/dbdata.c ******************/ |
| 8831 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -7998,13 +7998,16 @@ | |
| 7998 | while( rc==SQLITE_OK && sqlite3_step(pExplain)==SQLITE_ROW ){ |
| 7999 | /* int iId = sqlite3_column_int(pExplain, 0); */ |
| 8000 | /* int iParent = sqlite3_column_int(pExplain, 1); */ |
| 8001 | /* int iNotUsed = sqlite3_column_int(pExplain, 2); */ |
| 8002 | const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3); |
| 8003 | int nDetail; |
| 8004 | int i; |
| 8005 | |
| 8006 | if( !zDetail ) continue; |
| 8007 | nDetail = STRLEN(zDetail); |
| 8008 | |
| 8009 | for(i=0; i<nDetail; i++){ |
| 8010 | const char *zIdx = 0; |
| 8011 | if( memcmp(&zDetail[i], " USING INDEX ", 13)==0 ){ |
| 8012 | zIdx = &zDetail[i+13]; |
| 8013 | }else if( memcmp(&zDetail[i], " USING COVERING INDEX ", 22)==0 ){ |
| @@ -8820,11 +8823,11 @@ | |
| 8823 | sqlite3_free(p->zCandidates); |
| 8824 | sqlite3_free(p); |
| 8825 | } |
| 8826 | } |
| 8827 | |
| 8828 | #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 8829 | |
| 8830 | /************************* End ../ext/expert/sqlite3expert.c ********************/ |
| 8831 | |
| 8832 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 8833 | /************************* Begin ../ext/misc/dbdata.c ******************/ |
| 8834 |
+2
-2
| --- src/shun.c | ||
| +++ src/shun.c | ||
| @@ -187,11 +187,11 @@ | ||
| 187 | 187 | @ sight - set the "hidden" tag on such artifacts instead.</p> |
| 188 | 188 | @ |
| 189 | 189 | @ <blockquote> |
| 190 | 190 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 191 | 191 | login_insert_csrf_secret(); |
| 192 | - @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid"> | |
| 192 | + @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid"> | |
| 193 | 193 | if( zShun ){ |
| 194 | 194 | if( strlen(zShun) ){ |
| 195 | 195 | @ %h(zShun) |
| 196 | 196 | }else if( nRcvid ){ |
| 197 | 197 | db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
| @@ -214,11 +214,11 @@ | ||
| 214 | 214 | @ operations.</p> |
| 215 | 215 | @ |
| 216 | 216 | @ <blockquote> |
| 217 | 217 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 218 | 218 | login_insert_csrf_secret(); |
| 219 | - @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid"> | |
| 219 | + @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid"> | |
| 220 | 220 | if( zAccept ){ |
| 221 | 221 | if( strlen(zAccept) ){ |
| 222 | 222 | @ %h(zAccept) |
| 223 | 223 | }else if( nRcvid ){ |
| 224 | 224 | db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
| 225 | 225 |
| --- src/shun.c | |
| +++ src/shun.c | |
| @@ -187,11 +187,11 @@ | |
| 187 | @ sight - set the "hidden" tag on such artifacts instead.</p> |
| 188 | @ |
| 189 | @ <blockquote> |
| 190 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 191 | login_insert_csrf_secret(); |
| 192 | @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid"> |
| 193 | if( zShun ){ |
| 194 | if( strlen(zShun) ){ |
| 195 | @ %h(zShun) |
| 196 | }else if( nRcvid ){ |
| 197 | db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
| @@ -214,11 +214,11 @@ | |
| 214 | @ operations.</p> |
| 215 | @ |
| 216 | @ <blockquote> |
| 217 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 218 | login_insert_csrf_secret(); |
| 219 | @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid"> |
| 220 | if( zAccept ){ |
| 221 | if( strlen(zAccept) ){ |
| 222 | @ %h(zAccept) |
| 223 | }else if( nRcvid ){ |
| 224 | db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
| 225 |
| --- src/shun.c | |
| +++ src/shun.c | |
| @@ -187,11 +187,11 @@ | |
| 187 | @ sight - set the "hidden" tag on such artifacts instead.</p> |
| 188 | @ |
| 189 | @ <blockquote> |
| 190 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 191 | login_insert_csrf_secret(); |
| 192 | @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid"> |
| 193 | if( zShun ){ |
| 194 | if( strlen(zShun) ){ |
| 195 | @ %h(zShun) |
| 196 | }else if( nRcvid ){ |
| 197 | db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
| @@ -214,11 +214,11 @@ | |
| 214 | @ operations.</p> |
| 215 | @ |
| 216 | @ <blockquote> |
| 217 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 218 | login_insert_csrf_secret(); |
| 219 | @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid"> |
| 220 | if( zAccept ){ |
| 221 | if( strlen(zAccept) ){ |
| 222 | @ %h(zAccept) |
| 223 | }else if( nRcvid ){ |
| 224 | db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
| 225 |
+7
-2
| --- src/skins.c | ||
| +++ src/skins.c | ||
| @@ -281,10 +281,13 @@ | ||
| 281 | 281 | ** Return an identifier that is (probably) different for every skin |
| 282 | 282 | ** but that is (probably) the same if the skin is unchanged. This |
| 283 | 283 | ** identifier can be attached to resource URLs to force reloading when |
| 284 | 284 | ** the resources change but allow the resources to be read from cache |
| 285 | 285 | ** as long as they are unchanged. |
| 286 | +** | |
| 287 | +** The zResource argument is the name of a CONFIG setting that | |
| 288 | +** defines the resource. Examples: "css", "logo-image". | |
| 286 | 289 | */ |
| 287 | 290 | unsigned int skin_id(const char *zResource){ |
| 288 | 291 | unsigned int h = 0; |
| 289 | 292 | if( zAltSkinDir ){ |
| 290 | 293 | h = skin_hash(0, zAltSkinDir); |
| @@ -293,11 +296,13 @@ | ||
| 293 | 296 | }else{ |
| 294 | 297 | char *zMTime = db_get_mtime(zResource, 0, 0); |
| 295 | 298 | h = skin_hash(0, zMTime); |
| 296 | 299 | fossil_free(zMTime); |
| 297 | 300 | } |
| 298 | - h = skin_hash(h, MANIFEST_UUID); | |
| 301 | + | |
| 302 | + /* Change the ID every time Fossil is recompiled */ | |
| 303 | + h = skin_hash(h, fossil_exe_id()); | |
| 299 | 304 | return h; |
| 300 | 305 | } |
| 301 | 306 | |
| 302 | 307 | /* |
| 303 | 308 | ** For a skin named zSkinName, compute the name of the CONFIG table |
| @@ -1117,11 +1122,11 @@ | ||
| 1117 | 1122 | if( !g.perm.Admin ){ |
| 1118 | 1123 | @ <p>Only administrators are allowed to publish draft skins. Contact |
| 1119 | 1124 | @ an administrator to get this "draft%d(iSkin)" skin published.</p> |
| 1120 | 1125 | }else{ |
| 1121 | 1126 | @ <p>When the draft%d(iSkin) skin is ready for production use, |
| 1122 | - @ make it the default scan by clicking the acknowledgements and | |
| 1127 | + @ make it the default skin by clicking the acknowledgements and | |
| 1123 | 1128 | @ pressing the button below:</p> |
| 1124 | 1129 | @ |
| 1125 | 1130 | @ <form method='POST' action='%R/setup_skin#step7'> |
| 1126 | 1131 | @ <p class='skinInput'> |
| 1127 | 1132 | @ <input type='hidden' name='sk' value='%d(iSkin)'> |
| 1128 | 1133 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -281,10 +281,13 @@ | |
| 281 | ** Return an identifier that is (probably) different for every skin |
| 282 | ** but that is (probably) the same if the skin is unchanged. This |
| 283 | ** identifier can be attached to resource URLs to force reloading when |
| 284 | ** the resources change but allow the resources to be read from cache |
| 285 | ** as long as they are unchanged. |
| 286 | */ |
| 287 | unsigned int skin_id(const char *zResource){ |
| 288 | unsigned int h = 0; |
| 289 | if( zAltSkinDir ){ |
| 290 | h = skin_hash(0, zAltSkinDir); |
| @@ -293,11 +296,13 @@ | |
| 293 | }else{ |
| 294 | char *zMTime = db_get_mtime(zResource, 0, 0); |
| 295 | h = skin_hash(0, zMTime); |
| 296 | fossil_free(zMTime); |
| 297 | } |
| 298 | h = skin_hash(h, MANIFEST_UUID); |
| 299 | return h; |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | ** For a skin named zSkinName, compute the name of the CONFIG table |
| @@ -1117,11 +1122,11 @@ | |
| 1117 | if( !g.perm.Admin ){ |
| 1118 | @ <p>Only administrators are allowed to publish draft skins. Contact |
| 1119 | @ an administrator to get this "draft%d(iSkin)" skin published.</p> |
| 1120 | }else{ |
| 1121 | @ <p>When the draft%d(iSkin) skin is ready for production use, |
| 1122 | @ make it the default scan by clicking the acknowledgements and |
| 1123 | @ pressing the button below:</p> |
| 1124 | @ |
| 1125 | @ <form method='POST' action='%R/setup_skin#step7'> |
| 1126 | @ <p class='skinInput'> |
| 1127 | @ <input type='hidden' name='sk' value='%d(iSkin)'> |
| 1128 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -281,10 +281,13 @@ | |
| 281 | ** Return an identifier that is (probably) different for every skin |
| 282 | ** but that is (probably) the same if the skin is unchanged. This |
| 283 | ** identifier can be attached to resource URLs to force reloading when |
| 284 | ** the resources change but allow the resources to be read from cache |
| 285 | ** as long as they are unchanged. |
| 286 | ** |
| 287 | ** The zResource argument is the name of a CONFIG setting that |
| 288 | ** defines the resource. Examples: "css", "logo-image". |
| 289 | */ |
| 290 | unsigned int skin_id(const char *zResource){ |
| 291 | unsigned int h = 0; |
| 292 | if( zAltSkinDir ){ |
| 293 | h = skin_hash(0, zAltSkinDir); |
| @@ -293,11 +296,13 @@ | |
| 296 | }else{ |
| 297 | char *zMTime = db_get_mtime(zResource, 0, 0); |
| 298 | h = skin_hash(0, zMTime); |
| 299 | fossil_free(zMTime); |
| 300 | } |
| 301 | |
| 302 | /* Change the ID every time Fossil is recompiled */ |
| 303 | h = skin_hash(h, fossil_exe_id()); |
| 304 | return h; |
| 305 | } |
| 306 | |
| 307 | /* |
| 308 | ** For a skin named zSkinName, compute the name of the CONFIG table |
| @@ -1117,11 +1122,11 @@ | |
| 1122 | if( !g.perm.Admin ){ |
| 1123 | @ <p>Only administrators are allowed to publish draft skins. Contact |
| 1124 | @ an administrator to get this "draft%d(iSkin)" skin published.</p> |
| 1125 | }else{ |
| 1126 | @ <p>When the draft%d(iSkin) skin is ready for production use, |
| 1127 | @ make it the default skin by clicking the acknowledgements and |
| 1128 | @ pressing the button below:</p> |
| 1129 | @ |
| 1130 | @ <form method='POST' action='%R/setup_skin#step7'> |
| 1131 | @ <p class='skinInput'> |
| 1132 | @ <input type='hidden' name='sk' value='%d(iSkin)'> |
| 1133 |
+175
-92
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -1162,11 +1162,11 @@ | ||
| 1162 | 1162 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1163 | 1163 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1164 | 1164 | */ |
| 1165 | 1165 | #define SQLITE_VERSION "3.32.0" |
| 1166 | 1166 | #define SQLITE_VERSION_NUMBER 3032000 |
| 1167 | -#define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff" | |
| 1167 | +#define SQLITE_SOURCE_ID "2020-05-19 15:51:10 3117c1b5a9e348fd8d16ba9d03fdafaad8514567fb3403f72b86d6162ad40bde" | |
| 1168 | 1168 | |
| 1169 | 1169 | /* |
| 1170 | 1170 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1171 | 1171 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1172 | 1172 | ** |
| @@ -6532,11 +6532,11 @@ | ||
| 6532 | 6532 | ** when first called if N is less than or equal to zero or if a memory |
| 6533 | 6533 | ** allocate error occurs. |
| 6534 | 6534 | ** |
| 6535 | 6535 | ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is |
| 6536 | 6536 | ** determined by the N parameter on first successful call. Changing the |
| 6537 | -** value of N in any subsequents call to sqlite3_aggregate_context() within | |
| 6537 | +** value of N in any subsequent call to sqlite3_aggregate_context() within | |
| 6538 | 6538 | ** the same aggregate function instance will not resize the memory |
| 6539 | 6539 | ** allocation.)^ Within the xFinal callback, it is customary to set |
| 6540 | 6540 | ** N=0 in calls to sqlite3_aggregate_context(C,N) so that no |
| 6541 | 6541 | ** pointless memory allocations occur. |
| 6542 | 6542 | ** |
| @@ -17119,11 +17119,11 @@ | ||
| 17119 | 17119 | #define SQLITE_FUNC_EPHEM 0x0010 /* Ephemeral. Delete with VDBE */ |
| 17120 | 17120 | #define SQLITE_FUNC_NEEDCOLL 0x0020 /* sqlite3GetFuncCollSeq() might be called*/ |
| 17121 | 17121 | #define SQLITE_FUNC_LENGTH 0x0040 /* Built-in length() function */ |
| 17122 | 17122 | #define SQLITE_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */ |
| 17123 | 17123 | #define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */ |
| 17124 | -#define SQLITE_FUNC_COALESCE 0x0200 /* Built-in coalesce() or ifnull() */ | |
| 17124 | +/* 0x0200 -- available for reuse */ | |
| 17125 | 17125 | #define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */ |
| 17126 | 17126 | #define SQLITE_FUNC_CONSTANT 0x0800 /* Constant inputs give a constant output */ |
| 17127 | 17127 | #define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */ |
| 17128 | 17128 | #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a |
| 17129 | 17129 | ** single query - might change over time */ |
| @@ -17140,10 +17140,11 @@ | ||
| 17140 | 17140 | #define INLINEFUNC_coalesce 0 |
| 17141 | 17141 | #define INLINEFUNC_implies_nonnull_row 1 |
| 17142 | 17142 | #define INLINEFUNC_expr_implies_expr 2 |
| 17143 | 17143 | #define INLINEFUNC_expr_compare 3 |
| 17144 | 17144 | #define INLINEFUNC_affinity 4 |
| 17145 | +#define INLINEFUNC_iif 5 | |
| 17145 | 17146 | #define INLINEFUNC_unlikely 99 /* Default case */ |
| 17146 | 17147 | |
| 17147 | 17148 | /* |
| 17148 | 17149 | ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are |
| 17149 | 17150 | ** used to create the initializers for the FuncDef structures. |
| @@ -20064,12 +20065,14 @@ | ||
| 20064 | 20065 | # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) |
| 20065 | 20066 | #endif |
| 20066 | 20067 | SQLITE_PRIVATE int sqlite3ReadOnlyShadowTables(sqlite3 *db); |
| 20067 | 20068 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 20068 | 20069 | SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName); |
| 20070 | +SQLITE_PRIVATE int sqlite3IsShadowTableOf(sqlite3*,Table*,const char*); | |
| 20069 | 20071 | #else |
| 20070 | 20072 | # define sqlite3ShadowTableName(A,B) 0 |
| 20073 | +# define sqlite3IsShadowTableOf(A,B,C) 0 | |
| 20071 | 20074 | #endif |
| 20072 | 20075 | SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse*,Module*); |
| 20073 | 20076 | SQLITE_PRIVATE void sqlite3VtabEponymousTableClear(sqlite3*,Module*); |
| 20074 | 20077 | SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse*,Table*); |
| 20075 | 20078 | SQLITE_PRIVATE void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int); |
| @@ -27924,11 +27927,11 @@ | ||
| 27924 | 27927 | } |
| 27925 | 27928 | }else{ |
| 27926 | 27929 | assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); |
| 27927 | 27930 | assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); |
| 27928 | 27931 | sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
| 27929 | - pNew = sqlite3_realloc64(p, n); | |
| 27932 | + pNew = sqlite3Realloc(p, n); | |
| 27930 | 27933 | if( !pNew ){ |
| 27931 | 27934 | sqlite3OomFault(db); |
| 27932 | 27935 | } |
| 27933 | 27936 | sqlite3MemdebugSetType(pNew, |
| 27934 | 27937 | (db->lookaside.bDisable==0 ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP)); |
| @@ -29001,11 +29004,11 @@ | ||
| 29001 | 29004 | p->nAlloc = (int)szNew; |
| 29002 | 29005 | } |
| 29003 | 29006 | if( p->db ){ |
| 29004 | 29007 | zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc); |
| 29005 | 29008 | }else{ |
| 29006 | - zNew = sqlite3_realloc64(zOld, p->nAlloc); | |
| 29009 | + zNew = sqlite3Realloc(zOld, p->nAlloc); | |
| 29007 | 29010 | } |
| 29008 | 29011 | if( zNew ){ |
| 29009 | 29012 | assert( p->zText!=0 || p->nChar==0 ); |
| 29010 | 29013 | if( !isMalloced(p) && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar); |
| 29011 | 29014 | p->zText = zNew; |
| @@ -29343,11 +29346,11 @@ | ||
| 29343 | 29346 | ** and segfaults if you give it a long long int. |
| 29344 | 29347 | */ |
| 29345 | 29348 | SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){ |
| 29346 | 29349 | va_list ap; |
| 29347 | 29350 | StrAccum acc; |
| 29348 | - char zBuf[500]; | |
| 29351 | + char zBuf[SQLITE_PRINT_BUF_SIZE*10]; | |
| 29349 | 29352 | sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); |
| 29350 | 29353 | va_start(ap,zFormat); |
| 29351 | 29354 | sqlite3_str_vappendf(&acc, zFormat, ap); |
| 29352 | 29355 | va_end(ap); |
| 29353 | 29356 | sqlite3StrAccumFinish(&acc); |
| @@ -45048,10 +45051,11 @@ | ||
| 45048 | 45051 | } |
| 45049 | 45052 | |
| 45050 | 45053 | /* Forward references to VFS helper methods used for temporary files */ |
| 45051 | 45054 | static int winGetTempname(sqlite3_vfs *, char **); |
| 45052 | 45055 | static int winIsDir(const void *); |
| 45056 | +static BOOL winIsLongPathPrefix(const char *); | |
| 45053 | 45057 | static BOOL winIsDriveLetterAndColon(const char *); |
| 45054 | 45058 | |
| 45055 | 45059 | /* |
| 45056 | 45060 | ** Control and query of the open file handle. |
| 45057 | 45061 | */ |
| @@ -46817,11 +46821,13 @@ | ||
| 46817 | 46821 | pFile->pVfs = pVfs; |
| 46818 | 46822 | pFile->h = h; |
| 46819 | 46823 | if( isReadonly ){ |
| 46820 | 46824 | pFile->ctrlFlags |= WINFILE_RDONLY; |
| 46821 | 46825 | } |
| 46822 | - if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){ | |
| 46826 | + if( (flags & SQLITE_OPEN_MAIN_DB) | |
| 46827 | + && sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) | |
| 46828 | + ){ | |
| 46823 | 46829 | pFile->ctrlFlags |= WINFILE_PSOW; |
| 46824 | 46830 | } |
| 46825 | 46831 | pFile->lastErrno = NO_ERROR; |
| 46826 | 46832 | pFile->zPath = zName; |
| 46827 | 46833 | #if SQLITE_MAX_MMAP_SIZE>0 |
| @@ -47026,10 +47032,21 @@ | ||
| 47026 | 47032 | *pResOut = rc; |
| 47027 | 47033 | OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", |
| 47028 | 47034 | zFilename, pResOut, *pResOut)); |
| 47029 | 47035 | return SQLITE_OK; |
| 47030 | 47036 | } |
| 47037 | + | |
| 47038 | +/* | |
| 47039 | +** Returns non-zero if the specified path name starts with the "long path" | |
| 47040 | +** prefix. | |
| 47041 | +*/ | |
| 47042 | +static BOOL winIsLongPathPrefix( | |
| 47043 | + const char *zPathname | |
| 47044 | +){ | |
| 47045 | + return ( zPathname[0]=='\\' && zPathname[1]=='\\' | |
| 47046 | + && zPathname[2]=='?' && zPathname[3]=='\\' ); | |
| 47047 | +} | |
| 47031 | 47048 | |
| 47032 | 47049 | /* |
| 47033 | 47050 | ** Returns non-zero if the specified path name starts with a drive letter |
| 47034 | 47051 | ** followed by a colon character. |
| 47035 | 47052 | */ |
| @@ -47091,14 +47108,15 @@ | ||
| 47091 | 47108 | DWORD nByte; |
| 47092 | 47109 | void *zConverted; |
| 47093 | 47110 | char *zOut; |
| 47094 | 47111 | #endif |
| 47095 | 47112 | |
| 47096 | - /* If this path name begins with "/X:", where "X" is any alphabetic | |
| 47097 | - ** character, discard the initial "/" from the pathname. | |
| 47113 | + /* If this path name begins with "/X:" or "\\?\", where "X" is any | |
| 47114 | + ** alphabetic character, discard the initial "/" from the pathname. | |
| 47098 | 47115 | */ |
| 47099 | - if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){ | |
| 47116 | + if( zRelative[0]=='/' && (winIsDriveLetterAndColon(zRelative+1) | |
| 47117 | + || winIsLongPathPrefix(zRelative+1)) ){ | |
| 47100 | 47118 | zRelative++; |
| 47101 | 47119 | } |
| 47102 | 47120 | |
| 47103 | 47121 | #if defined(__CYGWIN__) |
| 47104 | 47122 | SimulateIOError( return SQLITE_ERROR ); |
| @@ -47850,11 +47868,11 @@ | ||
| 47850 | 47868 | if( newSz>p->szMax ){ |
| 47851 | 47869 | return SQLITE_FULL; |
| 47852 | 47870 | } |
| 47853 | 47871 | newSz *= 2; |
| 47854 | 47872 | if( newSz>p->szMax ) newSz = p->szMax; |
| 47855 | - pNew = sqlite3_realloc64(p->aData, newSz); | |
| 47873 | + pNew = sqlite3Realloc(p->aData, newSz); | |
| 47856 | 47874 | if( pNew==0 ) return SQLITE_NOMEM; |
| 47857 | 47875 | p->aData = pNew; |
| 47858 | 47876 | p->szAlloc = newSz; |
| 47859 | 47877 | return SQLITE_OK; |
| 47860 | 47878 | } |
| @@ -59820,11 +59838,11 @@ | ||
| 59820 | 59838 | |
| 59821 | 59839 | /* Enlarge the pWal->apWiData[] array if required */ |
| 59822 | 59840 | if( pWal->nWiData<=iPage ){ |
| 59823 | 59841 | sqlite3_int64 nByte = sizeof(u32*)*(iPage+1); |
| 59824 | 59842 | volatile u32 **apNew; |
| 59825 | - apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte); | |
| 59843 | + apNew = (volatile u32 **)sqlite3Realloc((void *)pWal->apWiData, nByte); | |
| 59826 | 59844 | if( !apNew ){ |
| 59827 | 59845 | *ppPage = 0; |
| 59828 | 59846 | return SQLITE_NOMEM_BKPT; |
| 59829 | 59847 | } |
| 59830 | 59848 | memset((void*)&apNew[pWal->nWiData], 0, |
| @@ -59941,29 +59959,47 @@ | ||
| 59941 | 59959 | |
| 59942 | 59960 | aOut[0] = s1; |
| 59943 | 59961 | aOut[1] = s2; |
| 59944 | 59962 | } |
| 59945 | 59963 | |
| 59964 | +/* | |
| 59965 | +** If there is the possibility of concurrent access to the SHM file | |
| 59966 | +** from multiple threads and/or processes, then do a memory barrier. | |
| 59967 | +*/ | |
| 59946 | 59968 | static void walShmBarrier(Wal *pWal){ |
| 59947 | 59969 | if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){ |
| 59948 | 59970 | sqlite3OsShmBarrier(pWal->pDbFd); |
| 59949 | 59971 | } |
| 59950 | 59972 | } |
| 59951 | 59973 | |
| 59974 | +/* | |
| 59975 | +** Add the SQLITE_NO_TSAN as part of the return-type of a function | |
| 59976 | +** definition as a hint that the function contains constructs that | |
| 59977 | +** might give false-positive TSAN warnings. | |
| 59978 | +** | |
| 59979 | +** See tag-20200519-1. | |
| 59980 | +*/ | |
| 59981 | +#if defined(__clang__) && !defined(SQLITE_NO_TSAN) | |
| 59982 | +# define SQLITE_NO_TSAN __attribute__((no_sanitize_thread)) | |
| 59983 | +#else | |
| 59984 | +# define SQLITE_NO_TSAN | |
| 59985 | +#endif | |
| 59986 | + | |
| 59952 | 59987 | /* |
| 59953 | 59988 | ** Write the header information in pWal->hdr into the wal-index. |
| 59954 | 59989 | ** |
| 59955 | 59990 | ** The checksum on pWal->hdr is updated before it is written. |
| 59956 | 59991 | */ |
| 59957 | -static void walIndexWriteHdr(Wal *pWal){ | |
| 59992 | +static SQLITE_NO_TSAN void walIndexWriteHdr(Wal *pWal){ | |
| 59958 | 59993 | volatile WalIndexHdr *aHdr = walIndexHdr(pWal); |
| 59959 | 59994 | const int nCksum = offsetof(WalIndexHdr, aCksum); |
| 59960 | 59995 | |
| 59961 | 59996 | assert( pWal->writeLock ); |
| 59962 | 59997 | pWal->hdr.isInit = 1; |
| 59963 | 59998 | pWal->hdr.iVersion = WALINDEX_MAX_VERSION; |
| 59964 | 59999 | walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum); |
| 60000 | + /* Possible TSAN false-positive. See tag-20200519-1 */ | |
| 59965 | 60001 | memcpy((void*)&aHdr[1], (const void*)&pWal->hdr, sizeof(WalIndexHdr)); |
| 59966 | 60002 | walShmBarrier(pWal); |
| 59967 | 60003 | memcpy((void*)&aHdr[0], (const void*)&pWal->hdr, sizeof(WalIndexHdr)); |
| 59968 | 60004 | } |
| 59969 | 60005 | |
| @@ -61074,11 +61110,11 @@ | ||
| 61074 | 61110 | pWal->nCkpt++; |
| 61075 | 61111 | pWal->hdr.mxFrame = 0; |
| 61076 | 61112 | sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); |
| 61077 | 61113 | memcpy(&pWal->hdr.aSalt[1], &salt1, 4); |
| 61078 | 61114 | walIndexWriteHdr(pWal); |
| 61079 | - pInfo->nBackfill = 0; | |
| 61115 | + AtomicStore(&pInfo->nBackfill, 0); | |
| 61080 | 61116 | pInfo->nBackfillAttempted = 0; |
| 61081 | 61117 | pInfo->aReadMark[1] = 0; |
| 61082 | 61118 | for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; |
| 61083 | 61119 | assert( pInfo->aReadMark[0]==0 ); |
| 61084 | 61120 | } |
| @@ -61149,36 +61185,17 @@ | ||
| 61149 | 61185 | ** cannot be backfilled from the WAL. |
| 61150 | 61186 | */ |
| 61151 | 61187 | mxSafeFrame = pWal->hdr.mxFrame; |
| 61152 | 61188 | mxPage = pWal->hdr.nPage; |
| 61153 | 61189 | for(i=1; i<WAL_NREADER; i++){ |
| 61154 | - /* Thread-sanitizer reports that the following is an unsafe read, | |
| 61155 | - ** as some other thread may be in the process of updating the value | |
| 61156 | - ** of the aReadMark[] slot. The assumption here is that if that is | |
| 61157 | - ** happening, the other client may only be increasing the value, | |
| 61158 | - ** not decreasing it. So assuming either that either the "old" or | |
| 61159 | - ** "new" version of the value is read, and not some arbitrary value | |
| 61160 | - ** that would never be written by a real client, things are still | |
| 61161 | - ** safe. | |
| 61162 | - ** | |
| 61163 | - ** Astute readers have pointed out that the assumption stated in the | |
| 61164 | - ** last sentence of the previous paragraph is not guaranteed to be | |
| 61165 | - ** true for all conforming systems. However, the assumption is true | |
| 61166 | - ** for all compilers and architectures in common use today (circa | |
| 61167 | - ** 2019-11-27) and the alternatives are both slow and complex, and | |
| 61168 | - ** so we will continue to go with the current design for now. If this | |
| 61169 | - ** bothers you, or if you really are running on a system where aligned | |
| 61170 | - ** 32-bit reads and writes are not atomic, then you can simply avoid | |
| 61171 | - ** the use of WAL mode, or only use WAL mode together with | |
| 61172 | - ** PRAGMA locking_mode=EXCLUSIVE and all will be well. | |
| 61173 | - */ | |
| 61174 | - u32 y = pInfo->aReadMark[i]; | |
| 61190 | + u32 y = AtomicLoad(pInfo->aReadMark+i); | |
| 61175 | 61191 | if( mxSafeFrame>y ){ |
| 61176 | 61192 | assert( y<=pWal->hdr.mxFrame ); |
| 61177 | 61193 | rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); |
| 61178 | 61194 | if( rc==SQLITE_OK ){ |
| 61179 | - pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED); | |
| 61195 | + u32 iMark = (i==1 ? mxSafeFrame : READMARK_NOT_USED); | |
| 61196 | + AtomicStore(pInfo->aReadMark+i, iMark); | |
| 61180 | 61197 | walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 61181 | 61198 | }else if( rc==SQLITE_BUSY ){ |
| 61182 | 61199 | mxSafeFrame = y; |
| 61183 | 61200 | xBusy = 0; |
| 61184 | 61201 | }else{ |
| @@ -61192,11 +61209,11 @@ | ||
| 61192 | 61209 | rc = walIteratorInit(pWal, pInfo->nBackfill, &pIter); |
| 61193 | 61210 | assert( rc==SQLITE_OK || pIter==0 ); |
| 61194 | 61211 | } |
| 61195 | 61212 | |
| 61196 | 61213 | if( pIter |
| 61197 | - && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK | |
| 61214 | + && (rc = walBusyLock(pWal,xBusy,pBusyArg,WAL_READ_LOCK(0),1))==SQLITE_OK | |
| 61198 | 61215 | ){ |
| 61199 | 61216 | u32 nBackfill = pInfo->nBackfill; |
| 61200 | 61217 | |
| 61201 | 61218 | pInfo->nBackfillAttempted = mxSafeFrame; |
| 61202 | 61219 | |
| @@ -61248,11 +61265,11 @@ | ||
| 61248 | 61265 | if( rc==SQLITE_OK ){ |
| 61249 | 61266 | rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags)); |
| 61250 | 61267 | } |
| 61251 | 61268 | } |
| 61252 | 61269 | if( rc==SQLITE_OK ){ |
| 61253 | - pInfo->nBackfill = mxSafeFrame; | |
| 61270 | + AtomicStore(&pInfo->nBackfill, mxSafeFrame); | |
| 61254 | 61271 | } |
| 61255 | 61272 | } |
| 61256 | 61273 | |
| 61257 | 61274 | /* Release the reader lock held while backfilling */ |
| 61258 | 61275 | walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); |
| @@ -61407,11 +61424,11 @@ | ||
| 61407 | 61424 | ** and *pChanged is set to 1. |
| 61408 | 61425 | ** |
| 61409 | 61426 | ** If the checksum cannot be verified return non-zero. If the header |
| 61410 | 61427 | ** is read successfully and the checksum verified, return zero. |
| 61411 | 61428 | */ |
| 61412 | -static int walIndexTryHdr(Wal *pWal, int *pChanged){ | |
| 61429 | +static SQLITE_NO_TSAN int walIndexTryHdr(Wal *pWal, int *pChanged){ | |
| 61413 | 61430 | u32 aCksum[2]; /* Checksum on the header content */ |
| 61414 | 61431 | WalIndexHdr h1, h2; /* Two copies of the header content */ |
| 61415 | 61432 | WalIndexHdr volatile *aHdr; /* Header in shared memory */ |
| 61416 | 61433 | |
| 61417 | 61434 | /* The first page of the wal-index must be mapped at this point. */ |
| @@ -61420,17 +61437,23 @@ | ||
| 61420 | 61437 | /* Read the header. This might happen concurrently with a write to the |
| 61421 | 61438 | ** same area of shared memory on a different CPU in a SMP, |
| 61422 | 61439 | ** meaning it is possible that an inconsistent snapshot is read |
| 61423 | 61440 | ** from the file. If this happens, return non-zero. |
| 61424 | 61441 | ** |
| 61442 | + ** tag-20200519-1: | |
| 61425 | 61443 | ** There are two copies of the header at the beginning of the wal-index. |
| 61426 | 61444 | ** When reading, read [0] first then [1]. Writes are in the reverse order. |
| 61427 | 61445 | ** Memory barriers are used to prevent the compiler or the hardware from |
| 61428 | - ** reordering the reads and writes. | |
| 61446 | + ** reordering the reads and writes. TSAN and similar tools can sometimes | |
| 61447 | + ** give false-positive warnings about these accesses because the tools do not | |
| 61448 | + ** account for the double-read and the memory barrier. The use of mutexes | |
| 61449 | + ** here would be problematic as the memory being accessed is potentially | |
| 61450 | + ** shared among multiple processes and not all mutex implementions work | |
| 61451 | + ** reliably in that environment. | |
| 61429 | 61452 | */ |
| 61430 | 61453 | aHdr = walIndexHdr(pWal); |
| 61431 | - memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); | |
| 61454 | + memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); /* Possible TSAN false-positive */ | |
| 61432 | 61455 | walShmBarrier(pWal); |
| 61433 | 61456 | memcpy(&h2, (void *)&aHdr[1], sizeof(h2)); |
| 61434 | 61457 | |
| 61435 | 61458 | if( memcmp(&h1, &h2, sizeof(h1))!=0 ){ |
| 61436 | 61459 | return 1; /* Dirty read */ |
| @@ -61871,11 +61894,11 @@ | ||
| 61871 | 61894 | } |
| 61872 | 61895 | |
| 61873 | 61896 | assert( pWal->nWiData>0 ); |
| 61874 | 61897 | assert( pWal->apWiData[0]!=0 ); |
| 61875 | 61898 | pInfo = walCkptInfo(pWal); |
| 61876 | - if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame | |
| 61899 | + if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame | |
| 61877 | 61900 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 61878 | 61901 | && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0) |
| 61879 | 61902 | #endif |
| 61880 | 61903 | ){ |
| 61881 | 61904 | /* The WAL has been completely backfilled (or it is empty). |
| @@ -62038,11 +62061,11 @@ | ||
| 62038 | 62061 | void *pBuf2 = sqlite3_malloc(szPage); |
| 62039 | 62062 | if( pBuf1==0 || pBuf2==0 ){ |
| 62040 | 62063 | rc = SQLITE_NOMEM; |
| 62041 | 62064 | }else{ |
| 62042 | 62065 | u32 i = pInfo->nBackfillAttempted; |
| 62043 | - for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){ | |
| 62066 | + for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){ | |
| 62044 | 62067 | WalHashLoc sLoc; /* Hash table location */ |
| 62045 | 62068 | u32 pgno; /* Page number in db file */ |
| 62046 | 62069 | i64 iDbOff; /* Offset of db file entry */ |
| 62047 | 62070 | i64 iWalOff; /* Offset of wal file entry */ |
| 62048 | 62071 | |
| @@ -62267,26 +62290,28 @@ | ||
| 62267 | 62290 | for(iHash=walFramePage(iLast); iHash>=iMinHash; iHash--){ |
| 62268 | 62291 | WalHashLoc sLoc; /* Hash table location */ |
| 62269 | 62292 | int iKey; /* Hash slot index */ |
| 62270 | 62293 | int nCollide; /* Number of hash collisions remaining */ |
| 62271 | 62294 | int rc; /* Error code */ |
| 62295 | + u32 iH; | |
| 62272 | 62296 | |
| 62273 | 62297 | rc = walHashGet(pWal, iHash, &sLoc); |
| 62274 | 62298 | if( rc!=SQLITE_OK ){ |
| 62275 | 62299 | return rc; |
| 62276 | 62300 | } |
| 62277 | 62301 | nCollide = HASHTABLE_NSLOT; |
| 62278 | - for(iKey=walHash(pgno); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){ | |
| 62279 | - u32 iH = sLoc.aHash[iKey]; | |
| 62302 | + iKey = walHash(pgno); | |
| 62303 | + while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){ | |
| 62280 | 62304 | u32 iFrame = iH + sLoc.iZero; |
| 62281 | 62305 | if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH]==pgno ){ |
| 62282 | 62306 | assert( iFrame>iRead || CORRUPT_DB ); |
| 62283 | 62307 | iRead = iFrame; |
| 62284 | 62308 | } |
| 62285 | 62309 | if( (nCollide--)==0 ){ |
| 62286 | 62310 | return SQLITE_CORRUPT_BKPT; |
| 62287 | 62311 | } |
| 62312 | + iKey = walNextHash(iKey); | |
| 62288 | 62313 | } |
| 62289 | 62314 | if( iRead ) break; |
| 62290 | 62315 | } |
| 62291 | 62316 | |
| 62292 | 62317 | #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT |
| @@ -79307,10 +79332,11 @@ | ||
| 79307 | 79332 | char *zP4; |
| 79308 | 79333 | char *zCom; |
| 79309 | 79334 | sqlite3 dummyDb; |
| 79310 | 79335 | static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n"; |
| 79311 | 79336 | if( pOut==0 ) pOut = stdout; |
| 79337 | + sqlite3BeginBenignMalloc(); | |
| 79312 | 79338 | dummyDb.mallocFailed = 1; |
| 79313 | 79339 | zP4 = sqlite3VdbeDisplayP4(&dummyDb, pOp); |
| 79314 | 79340 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 79315 | 79341 | zCom = sqlite3VdbeDisplayComment(0, pOp, zP4); |
| 79316 | 79342 | #else |
| @@ -79325,10 +79351,11 @@ | ||
| 79325 | 79351 | zCom ? zCom : "" |
| 79326 | 79352 | ); |
| 79327 | 79353 | fflush(pOut); |
| 79328 | 79354 | sqlite3_free(zP4); |
| 79329 | 79355 | sqlite3_free(zCom); |
| 79356 | + sqlite3EndBenignMalloc(); | |
| 79330 | 79357 | } |
| 79331 | 79358 | #endif |
| 79332 | 79359 | |
| 79333 | 79360 | /* |
| 79334 | 79361 | ** Initialize an array of N Mem element. |
| @@ -84066,11 +84093,11 @@ | ||
| 84066 | 84093 | p->db->errCode = SQLITE_OK; |
| 84067 | 84094 | |
| 84068 | 84095 | /* If the bit corresponding to this variable in Vdbe.expmask is set, then |
| 84069 | 84096 | ** binding a new value to this variable invalidates the current query plan. |
| 84070 | 84097 | ** |
| 84071 | - ** IMPLEMENTATION-OF: R-48440-37595 If the specific value bound to host | |
| 84098 | + ** IMPLEMENTATION-OF: R-57496-20354 If the specific value bound to a host | |
| 84072 | 84099 | ** parameter in the WHERE clause might influence the choice of query plan |
| 84073 | 84100 | ** for a statement, then the statement will be automatically recompiled, |
| 84074 | 84101 | ** as if there had been a schema change, on the first sqlite3_step() call |
| 84075 | 84102 | ** following any change to the bindings of that parameter. |
| 84076 | 84103 | */ |
| @@ -96365,12 +96392,12 @@ | ||
| 96365 | 96392 | ************************************************************************* |
| 96366 | 96393 | ** |
| 96367 | 96394 | ** This file implements virtual-tables for examining the bytecode content |
| 96368 | 96395 | ** of a prepared statement. |
| 96369 | 96396 | */ |
| 96370 | -#ifdef SQLITE_ENABLE_BYTECODE_VTAB | |
| 96371 | 96397 | /* #include "sqliteInt.h" */ |
| 96398 | +#if defined(SQLITE_ENABLE_BYTECODE_VTAB) && !defined(SQLITE_OMIT_VIRTUALTABLE) | |
| 96372 | 96399 | /* #include "vdbeInt.h" */ |
| 96373 | 96400 | |
| 96374 | 96401 | /* An instance of the bytecode() table-valued function. |
| 96375 | 96402 | */ |
| 96376 | 96403 | typedef struct bytecodevtab bytecodevtab; |
| @@ -96771,10 +96798,12 @@ | ||
| 96771 | 96798 | if( rc==SQLITE_OK ){ |
| 96772 | 96799 | rc = sqlite3_create_module(db, "tables_used", &bytecodevtabModule, &db); |
| 96773 | 96800 | } |
| 96774 | 96801 | return rc; |
| 96775 | 96802 | } |
| 96803 | +#elif defined(SQLITE_ENABLE_BYTECODE_VTAB) | |
| 96804 | +SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3 *db){ return SQLITE_OK; } | |
| 96776 | 96805 | #endif /* SQLITE_ENABLE_BYTECODE_VTAB */ |
| 96777 | 96806 | |
| 96778 | 96807 | /************** End of vdbevtab.c ********************************************/ |
| 96779 | 96808 | /************** Begin file memjournal.c **************************************/ |
| 96780 | 96809 | /* |
| @@ -103075,10 +103104,17 @@ | ||
| 103075 | 103104 | } |
| 103076 | 103105 | setDoNotMergeFlagOnCopy(v); |
| 103077 | 103106 | sqlite3VdbeResolveLabel(v, endCoalesce); |
| 103078 | 103107 | break; |
| 103079 | 103108 | } |
| 103109 | + case INLINEFUNC_iif: { | |
| 103110 | + Expr caseExpr; | |
| 103111 | + memset(&caseExpr, 0, sizeof(caseExpr)); | |
| 103112 | + caseExpr.op = TK_CASE; | |
| 103113 | + caseExpr.x.pList = pFarg; | |
| 103114 | + return sqlite3ExprCodeTarget(pParse, &caseExpr, target); | |
| 103115 | + } | |
| 103080 | 103116 | |
| 103081 | 103117 | default: { |
| 103082 | 103118 | /* The UNLIKELY() function is a no-op. The result is the value |
| 103083 | 103119 | ** of the first argument. |
| 103084 | 103120 | */ |
| @@ -105497,11 +105533,14 @@ | ||
| 105497 | 105533 | if( !zName ) goto exit_rename_table; |
| 105498 | 105534 | |
| 105499 | 105535 | /* Check that a table or index named 'zName' does not already exist |
| 105500 | 105536 | ** in database iDb. If so, this is an error. |
| 105501 | 105537 | */ |
| 105502 | - if( sqlite3FindTable(db, zName, zDb) || sqlite3FindIndex(db, zName, zDb) ){ | |
| 105538 | + if( sqlite3FindTable(db, zName, zDb) | |
| 105539 | + || sqlite3FindIndex(db, zName, zDb) | |
| 105540 | + || sqlite3IsShadowTableOf(db, pTab, zName) | |
| 105541 | + ){ | |
| 105503 | 105542 | sqlite3ErrorMsg(pParse, |
| 105504 | 105543 | "there is already another table or index with this name: %s", zName); |
| 105505 | 105544 | goto exit_rename_table; |
| 105506 | 105545 | } |
| 105507 | 105546 | |
| @@ -110244,26 +110283,43 @@ | ||
| 110244 | 110283 | ** exists */ |
| 110245 | 110284 | if( db->auth.authLevel<UAUTH_Admin && sqlite3UserAuthTable(zName)!=0 ){ |
| 110246 | 110285 | return 0; |
| 110247 | 110286 | } |
| 110248 | 110287 | #endif |
| 110249 | - while(1){ | |
| 110250 | - for(i=OMIT_TEMPDB; i<db->nDb; i++){ | |
| 110251 | - int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ | |
| 110252 | - if( zDatabase==0 || sqlite3DbIsNamed(db, j, zDatabase) ){ | |
| 110253 | - assert( sqlite3SchemaMutexHeld(db, j, 0) ); | |
| 110254 | - p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName); | |
| 110255 | - if( p ) return p; | |
| 110256 | - } | |
| 110257 | - } | |
| 110258 | - /* Not found. If the name we were looking for was temp.sqlite_master | |
| 110259 | - ** then change the name to sqlite_temp_master and try again. */ | |
| 110260 | - if( sqlite3StrICmp(zName, MASTER_NAME)!=0 ) break; | |
| 110261 | - if( sqlite3_stricmp(zDatabase, db->aDb[1].zDbSName)!=0 ) break; | |
| 110262 | - zName = TEMP_MASTER_NAME; | |
| 110263 | - } | |
| 110264 | - return 0; | |
| 110288 | + if( zDatabase ){ | |
| 110289 | + for(i=0; i<db->nDb; i++){ | |
| 110290 | + if( sqlite3StrICmp(zDatabase, db->aDb[i].zDbSName)==0 ) break; | |
| 110291 | + } | |
| 110292 | + if( i>=db->nDb ){ | |
| 110293 | + /* No match against the official names. But always match "main" | |
| 110294 | + ** to schema 0 as a legacy fallback. */ | |
| 110295 | + if( sqlite3StrICmp(zDatabase,"main")==0 ){ | |
| 110296 | + i = 0; | |
| 110297 | + }else{ | |
| 110298 | + return 0; | |
| 110299 | + } | |
| 110300 | + } | |
| 110301 | + p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName); | |
| 110302 | + if( p==0 && i==1 && sqlite3StrICmp(zName, MASTER_NAME)==0 ){ | |
| 110303 | + /* All temp.sqlite_master to be an alias for sqlite_temp_master */ | |
| 110304 | + p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash, TEMP_MASTER_NAME); | |
| 110305 | + } | |
| 110306 | + }else{ | |
| 110307 | + /* Match against TEMP first */ | |
| 110308 | + p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash, zName); | |
| 110309 | + if( p ) return p; | |
| 110310 | + /* The main database is second */ | |
| 110311 | + p = sqlite3HashFind(&db->aDb[0].pSchema->tblHash, zName); | |
| 110312 | + if( p ) return p; | |
| 110313 | + /* Attached databases are in order of attachment */ | |
| 110314 | + for(i=2; i<db->nDb; i++){ | |
| 110315 | + assert( sqlite3SchemaMutexHeld(db, i, 0) ); | |
| 110316 | + p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName); | |
| 110317 | + if( p ) break; | |
| 110318 | + } | |
| 110319 | + } | |
| 110320 | + return p; | |
| 110265 | 110321 | } |
| 110266 | 110322 | |
| 110267 | 110323 | /* |
| 110268 | 110324 | ** Locate the in-memory structure that describes a particular database |
| 110269 | 110325 | ** table given the name of that table and (optionally) the name of the |
| @@ -112063,10 +112119,32 @@ | ||
| 112063 | 112119 | assert( pPk->nColumn==j ); |
| 112064 | 112120 | assert( pTab->nNVCol<=j ); |
| 112065 | 112121 | recomputeColumnsNotIndexed(pPk); |
| 112066 | 112122 | } |
| 112067 | 112123 | |
| 112124 | + | |
| 112125 | +#ifndef SQLITE_OMIT_VIRTUALTABLE | |
| 112126 | +/* | |
| 112127 | +** Return true if pTab is a virtual table and zName is a shadow table name | |
| 112128 | +** for that virtual table. | |
| 112129 | +*/ | |
| 112130 | +SQLITE_PRIVATE int sqlite3IsShadowTableOf(sqlite3 *db, Table *pTab, const char *zName){ | |
| 112131 | + int nName; /* Length of zName */ | |
| 112132 | + Module *pMod; /* Module for the virtual table */ | |
| 112133 | + | |
| 112134 | + if( !IsVirtual(pTab) ) return 0; | |
| 112135 | + nName = sqlite3Strlen30(pTab->zName); | |
| 112136 | + if( sqlite3_strnicmp(zName, pTab->zName, nName)!=0 ) return 0; | |
| 112137 | + if( zName[nName]!='_' ) return 0; | |
| 112138 | + pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]); | |
| 112139 | + if( pMod==0 ) return 0; | |
| 112140 | + if( pMod->pModule->iVersion<3 ) return 0; | |
| 112141 | + if( pMod->pModule->xShadowName==0 ) return 0; | |
| 112142 | + return pMod->pModule->xShadowName(zName+nName+1); | |
| 112143 | +} | |
| 112144 | +#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ | |
| 112145 | + | |
| 112068 | 112146 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 112069 | 112147 | /* |
| 112070 | 112148 | ** Return true if zName is a shadow table name in the current database |
| 112071 | 112149 | ** connection. |
| 112072 | 112150 | ** |
| @@ -112074,26 +112152,21 @@ | ||
| 112074 | 112152 | ** restored to its original value prior to this routine returning. |
| 112075 | 112153 | */ |
| 112076 | 112154 | SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName){ |
| 112077 | 112155 | char *zTail; /* Pointer to the last "_" in zName */ |
| 112078 | 112156 | Table *pTab; /* Table that zName is a shadow of */ |
| 112079 | - Module *pMod; /* Module for the virtual table */ | |
| 112080 | - | |
| 112081 | 112157 | zTail = strrchr(zName, '_'); |
| 112082 | 112158 | if( zTail==0 ) return 0; |
| 112083 | 112159 | *zTail = 0; |
| 112084 | 112160 | pTab = sqlite3FindTable(db, zName, 0); |
| 112085 | 112161 | *zTail = '_'; |
| 112086 | 112162 | if( pTab==0 ) return 0; |
| 112087 | 112163 | if( !IsVirtual(pTab) ) return 0; |
| 112088 | - pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]); | |
| 112089 | - if( pMod==0 ) return 0; | |
| 112090 | - if( pMod->pModule->iVersion<3 ) return 0; | |
| 112091 | - if( pMod->pModule->xShadowName==0 ) return 0; | |
| 112092 | - return pMod->pModule->xShadowName(zTail+1); | |
| 112164 | + return sqlite3IsShadowTableOf(db, pTab, zName); | |
| 112093 | 112165 | } |
| 112094 | 112166 | #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 112167 | + | |
| 112095 | 112168 | |
| 112096 | 112169 | #ifdef SQLITE_DEBUG |
| 112097 | 112170 | /* |
| 112098 | 112171 | ** Mark all nodes of an expression as EP_Immutable, indicating that |
| 112099 | 112172 | ** they should not be changed. Expressions attached to a table or |
| @@ -117780,11 +117853,11 @@ | ||
| 117780 | 117853 | if( (cntExpand&(cntExpand-1))==0 ){ |
| 117781 | 117854 | /* Grow the size of the output buffer only on substitutions |
| 117782 | 117855 | ** whose index is a power of two: 1, 2, 4, 8, 16, 32, ... */ |
| 117783 | 117856 | u8 *zOld; |
| 117784 | 117857 | zOld = zOut; |
| 117785 | - zOut = sqlite3_realloc64(zOut, (int)nOut + (nOut - nStr - 1)); | |
| 117858 | + zOut = sqlite3Realloc(zOut, (int)nOut + (nOut - nStr - 1)); | |
| 117786 | 117859 | if( zOut==0 ){ |
| 117787 | 117860 | sqlite3_result_error_nomem(context); |
| 117788 | 117861 | sqlite3_free(zOld); |
| 117789 | 117862 | return; |
| 117790 | 117863 | } |
| @@ -118477,11 +118550,11 @@ | ||
| 118477 | 118550 | FUNCTION(round, 2, 0, 0, roundFunc ), |
| 118478 | 118551 | #endif |
| 118479 | 118552 | FUNCTION(upper, 1, 0, 0, upperFunc ), |
| 118480 | 118553 | FUNCTION(lower, 1, 0, 0, lowerFunc ), |
| 118481 | 118554 | FUNCTION(hex, 1, 0, 0, hexFunc ), |
| 118482 | - INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, SQLITE_FUNC_COALESCE), | |
| 118555 | + INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ), | |
| 118483 | 118556 | VFUNCTION(random, 0, 0, 0, randomFunc ), |
| 118484 | 118557 | VFUNCTION(randomblob, 1, 0, 0, randomBlob ), |
| 118485 | 118558 | FUNCTION(nullif, 2, 0, 1, nullifFunc ), |
| 118486 | 118559 | DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ), |
| 118487 | 118560 | DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), |
| @@ -118517,11 +118590,12 @@ | ||
| 118517 | 118590 | #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION |
| 118518 | 118591 | FUNCTION(unknown, -1, 0, 0, unknownFunc ), |
| 118519 | 118592 | #endif |
| 118520 | 118593 | FUNCTION(coalesce, 1, 0, 0, 0 ), |
| 118521 | 118594 | FUNCTION(coalesce, 0, 0, 0, 0 ), |
| 118522 | - INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, SQLITE_FUNC_COALESCE), | |
| 118595 | + INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ), | |
| 118596 | + INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ), | |
| 118523 | 118597 | }; |
| 118524 | 118598 | #ifndef SQLITE_OMIT_ALTERTABLE |
| 118525 | 118599 | sqlite3AlterFunctions(); |
| 118526 | 118600 | #endif |
| 118527 | 118601 | sqlite3WindowFunctions(); |
| @@ -135570,11 +135644,11 @@ | ||
| 135570 | 135644 | need = nCol; |
| 135571 | 135645 | } |
| 135572 | 135646 | if( p->nData + need > p->nAlloc ){ |
| 135573 | 135647 | char **azNew; |
| 135574 | 135648 | p->nAlloc = p->nAlloc*2 + need; |
| 135575 | - azNew = sqlite3_realloc64( p->azResult, sizeof(char*)*p->nAlloc ); | |
| 135649 | + azNew = sqlite3Realloc( p->azResult, sizeof(char*)*p->nAlloc ); | |
| 135576 | 135650 | if( azNew==0 ) goto malloc_failed; |
| 135577 | 135651 | p->azResult = azNew; |
| 135578 | 135652 | } |
| 135579 | 135653 | |
| 135580 | 135654 | /* If this is the first row, then generate an extra row containing |
| @@ -135679,11 +135753,11 @@ | ||
| 135679 | 135753 | sqlite3_free_table(&res.azResult[1]); |
| 135680 | 135754 | return rc; |
| 135681 | 135755 | } |
| 135682 | 135756 | if( res.nAlloc>res.nData ){ |
| 135683 | 135757 | char **azNew; |
| 135684 | - azNew = sqlite3_realloc64( res.azResult, sizeof(char*)*res.nData ); | |
| 135758 | + azNew = sqlite3Realloc( res.azResult, sizeof(char*)*res.nData ); | |
| 135685 | 135759 | if( azNew==0 ){ |
| 135686 | 135760 | sqlite3_free_table(&res.azResult[1]); |
| 135687 | 135761 | db->errCode = SQLITE_NOMEM; |
| 135688 | 135762 | return SQLITE_NOMEM_BKPT; |
| 135689 | 135763 | } |
| @@ -136969,14 +137043,14 @@ | ||
| 136969 | 137043 | ** Therefore, the P4 parameter is only required if the default value for |
| 136970 | 137044 | ** the column is a literal number, string or null. The sqlite3ValueFromExpr() |
| 136971 | 137045 | ** function is capable of transforming these types of expressions into |
| 136972 | 137046 | ** sqlite3_value objects. |
| 136973 | 137047 | ** |
| 136974 | -** If parameter iReg is not negative, code an OP_RealAffinity instruction | |
| 136975 | -** on register iReg. This is used when an equivalent integer value is | |
| 136976 | -** stored in place of an 8-byte floating point value in order to save | |
| 136977 | -** space. | |
| 137048 | +** If column as REAL affinity and the table is an ordinary b-tree table | |
| 137049 | +** (not a virtual table) then the value might have been stored as an | |
| 137050 | +** integer. In that case, add an OP_RealAffinity opcode to make sure | |
| 137051 | +** it has been converted into REAL. | |
| 136978 | 137052 | */ |
| 136979 | 137053 | SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ |
| 136980 | 137054 | assert( pTab!=0 ); |
| 136981 | 137055 | if( !pTab->pSelect ){ |
| 136982 | 137056 | sqlite3_value *pValue = 0; |
| @@ -136989,11 +137063,11 @@ | ||
| 136989 | 137063 | if( pValue ){ |
| 136990 | 137064 | sqlite3VdbeAppendP4(v, pValue, P4_MEM); |
| 136991 | 137065 | } |
| 136992 | 137066 | } |
| 136993 | 137067 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 136994 | - if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ | |
| 137068 | + if( pTab->aCol[i].affinity==SQLITE_AFF_REAL && !IsVirtual(pTab) ){ | |
| 136995 | 137069 | sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); |
| 136996 | 137070 | } |
| 136997 | 137071 | #endif |
| 136998 | 137072 | } |
| 136999 | 137073 | |
| @@ -139838,11 +139912,11 @@ | ||
| 139838 | 139912 | assert( IsVirtual(pTab) ); |
| 139839 | 139913 | for(i=0; i<pToplevel->nVtabLock; i++){ |
| 139840 | 139914 | if( pTab==pToplevel->apVtabLock[i] ) return; |
| 139841 | 139915 | } |
| 139842 | 139916 | n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]); |
| 139843 | - apVtabLock = sqlite3_realloc64(pToplevel->apVtabLock, n); | |
| 139917 | + apVtabLock = sqlite3Realloc(pToplevel->apVtabLock, n); | |
| 139844 | 139918 | if( apVtabLock ){ |
| 139845 | 139919 | pToplevel->apVtabLock = apVtabLock; |
| 139846 | 139920 | pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab; |
| 139847 | 139921 | }else{ |
| 139848 | 139922 | sqlite3OomFault(pToplevel->db); |
| @@ -151130,17 +151204,23 @@ | ||
| 151130 | 151204 | ){ |
| 151131 | 151205 | if( pAppend ){ |
| 151132 | 151206 | int i; |
| 151133 | 151207 | int nInit = pList ? pList->nExpr : 0; |
| 151134 | 151208 | for(i=0; i<pAppend->nExpr; i++){ |
| 151135 | - int iDummy; | |
| 151136 | 151209 | Expr *pDup = sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0); |
| 151137 | 151210 | assert( pDup==0 || !ExprHasProperty(pDup, EP_MemToken) ); |
| 151138 | - if( bIntToNull && pDup && sqlite3ExprIsInteger(pDup, &iDummy) ){ | |
| 151139 | - pDup->op = TK_NULL; | |
| 151140 | - pDup->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse); | |
| 151141 | - pDup->u.zToken = 0; | |
| 151211 | + if( bIntToNull && pDup ){ | |
| 151212 | + int iDummy; | |
| 151213 | + Expr *pSub; | |
| 151214 | + for(pSub=pDup; ExprHasProperty(pSub, EP_Skip); pSub=pSub->pLeft){ | |
| 151215 | + assert( pSub ); | |
| 151216 | + } | |
| 151217 | + if( sqlite3ExprIsInteger(pSub, &iDummy) ){ | |
| 151218 | + pSub->op = TK_NULL; | |
| 151219 | + pSub->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse); | |
| 151220 | + pSub->u.zToken = 0; | |
| 151221 | + } | |
| 151142 | 151222 | } |
| 151143 | 151223 | pList = sqlite3ExprListAppend(pParse, pList, pDup); |
| 151144 | 151224 | if( pList ) pList->a[nInit+i].sortFlags = pAppend->a[i].sortFlags; |
| 151145 | 151225 | } |
| 151146 | 151226 | } |
| @@ -160322,10 +160402,11 @@ | ||
| 160322 | 160402 | } |
| 160323 | 160403 | #endif |
| 160324 | 160404 | if( rc==SQLITE_OK ){ |
| 160325 | 160405 | sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, |
| 160326 | 160406 | sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); |
| 160407 | + sqlite3MemoryBarrier(); | |
| 160327 | 160408 | sqlite3GlobalConfig.isInit = 1; |
| 160328 | 160409 | #ifdef SQLITE_EXTRA_INIT |
| 160329 | 160410 | bRunExtraInit = 1; |
| 160330 | 160411 | #endif |
| 160331 | 160412 | } |
| @@ -163270,11 +163351,11 @@ | ||
| 163270 | 163351 | assert( SQLITE_OPEN_CREATE == 0x04 ); |
| 163271 | 163352 | testcase( (1<<(flags&7))==0x02 ); /* READONLY */ |
| 163272 | 163353 | testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ |
| 163273 | 163354 | testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ |
| 163274 | 163355 | if( ((1<<(flags&7)) & 0x46)==0 ){ |
| 163275 | - rc = SQLITE_MISUSE_BKPT; /* IMP: R-65497-44594 */ | |
| 163356 | + rc = SQLITE_MISUSE_BKPT; /* IMP: R-18321-05872 */ | |
| 163276 | 163357 | }else{ |
| 163277 | 163358 | rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); |
| 163278 | 163359 | } |
| 163279 | 163360 | if( rc!=SQLITE_OK ){ |
| 163280 | 163361 | if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); |
| @@ -171194,10 +171275,11 @@ | ||
| 171194 | 171275 | while( *pRc==SQLITE_OK && pLeft->bEof==0 ){ |
| 171195 | 171276 | memset(pDl->pList, 0, pDl->nList); |
| 171196 | 171277 | fts3EvalNextRow(pCsr, pLeft, pRc); |
| 171197 | 171278 | } |
| 171198 | 171279 | } |
| 171280 | + pRight->bEof = pLeft->bEof = 1; | |
| 171199 | 171281 | } |
| 171200 | 171282 | } |
| 171201 | 171283 | break; |
| 171202 | 171284 | } |
| 171203 | 171285 | |
| @@ -182739,11 +182821,11 @@ | ||
| 182739 | 182821 | iStart = pExpr->iPhrase * p->nCol; |
| 182740 | 182822 | }else{ |
| 182741 | 182823 | iStart = pExpr->iPhrase * ((p->nCol + 31) / 32); |
| 182742 | 182824 | } |
| 182743 | 182825 | |
| 182744 | - while( 1 ){ | |
| 182826 | + if( pIter ) while( 1 ){ | |
| 182745 | 182827 | int nHit = fts3ColumnlistCount(&pIter); |
| 182746 | 182828 | if( (pPhrase->iColumn>=pTab->nColumn || pPhrase->iColumn==iCol) ){ |
| 182747 | 182829 | if( p->flag==FTS3_MATCHINFO_LHITS ){ |
| 182748 | 182830 | p->aMatchinfo[iStart + iCol] = (u32)nHit; |
| 182749 | 182831 | }else if( nHit ){ |
| @@ -184653,10 +184735,11 @@ | ||
| 184653 | 184735 | } |
| 184654 | 184736 | |
| 184655 | 184737 | /* Append N bytes from zIn onto the end of the JsonString string. |
| 184656 | 184738 | */ |
| 184657 | 184739 | static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ |
| 184740 | + if( N==0 ) return; | |
| 184658 | 184741 | if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return; |
| 184659 | 184742 | memcpy(p->zBuf+p->nUsed, zIn, N); |
| 184660 | 184743 | p->nUsed += N; |
| 184661 | 184744 | } |
| 184662 | 184745 | |
| @@ -224664,11 +224747,11 @@ | ||
| 224664 | 224747 | int nArg, /* Number of args */ |
| 224665 | 224748 | sqlite3_value **apUnused /* Function arguments */ |
| 224666 | 224749 | ){ |
| 224667 | 224750 | assert( nArg==0 ); |
| 224668 | 224751 | UNUSED_PARAM2(nArg, apUnused); |
| 224669 | - sqlite3_result_text(pCtx, "fts5: 2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff", -1, SQLITE_TRANSIENT); | |
| 224752 | + sqlite3_result_text(pCtx, "fts5: 2020-05-19 15:51:10 3117c1b5a9e348fd8d16ba9d03fdafaad8514567fb3403f72b86d6162ad40bde", -1, SQLITE_TRANSIENT); | |
| 224670 | 224753 | } |
| 224671 | 224754 | |
| 224672 | 224755 | /* |
| 224673 | 224756 | ** Return true if zName is the extension on one of the shadow tables used |
| 224674 | 224757 | ** by this module. |
| @@ -229447,12 +229530,12 @@ | ||
| 229447 | 229530 | } |
| 229448 | 229531 | #endif /* SQLITE_CORE */ |
| 229449 | 229532 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 229450 | 229533 | |
| 229451 | 229534 | /************** End of stmt.c ************************************************/ |
| 229452 | -#if __LINE__!=229452 | |
| 229535 | +#if __LINE__!=229535 | |
| 229453 | 229536 | #undef SQLITE_SOURCE_ID |
| 229454 | -#define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f96alt2" | |
| 229537 | +#define SQLITE_SOURCE_ID "2020-05-19 15:51:10 3117c1b5a9e348fd8d16ba9d03fdafaad8514567fb3403f72b86d6162ad4alt2" | |
| 229455 | 229538 | #endif |
| 229456 | 229539 | /* Return the source-id for this library */ |
| 229457 | 229540 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 229458 | 229541 | /************************** End of sqlite3.c ******************************/ |
| 229459 | 229542 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1162,11 +1162,11 @@ | |
| 1162 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1163 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1164 | */ |
| 1165 | #define SQLITE_VERSION "3.32.0" |
| 1166 | #define SQLITE_VERSION_NUMBER 3032000 |
| 1167 | #define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff" |
| 1168 | |
| 1169 | /* |
| 1170 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1171 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1172 | ** |
| @@ -6532,11 +6532,11 @@ | |
| 6532 | ** when first called if N is less than or equal to zero or if a memory |
| 6533 | ** allocate error occurs. |
| 6534 | ** |
| 6535 | ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is |
| 6536 | ** determined by the N parameter on first successful call. Changing the |
| 6537 | ** value of N in any subsequents call to sqlite3_aggregate_context() within |
| 6538 | ** the same aggregate function instance will not resize the memory |
| 6539 | ** allocation.)^ Within the xFinal callback, it is customary to set |
| 6540 | ** N=0 in calls to sqlite3_aggregate_context(C,N) so that no |
| 6541 | ** pointless memory allocations occur. |
| 6542 | ** |
| @@ -17119,11 +17119,11 @@ | |
| 17119 | #define SQLITE_FUNC_EPHEM 0x0010 /* Ephemeral. Delete with VDBE */ |
| 17120 | #define SQLITE_FUNC_NEEDCOLL 0x0020 /* sqlite3GetFuncCollSeq() might be called*/ |
| 17121 | #define SQLITE_FUNC_LENGTH 0x0040 /* Built-in length() function */ |
| 17122 | #define SQLITE_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */ |
| 17123 | #define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */ |
| 17124 | #define SQLITE_FUNC_COALESCE 0x0200 /* Built-in coalesce() or ifnull() */ |
| 17125 | #define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */ |
| 17126 | #define SQLITE_FUNC_CONSTANT 0x0800 /* Constant inputs give a constant output */ |
| 17127 | #define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */ |
| 17128 | #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a |
| 17129 | ** single query - might change over time */ |
| @@ -17140,10 +17140,11 @@ | |
| 17140 | #define INLINEFUNC_coalesce 0 |
| 17141 | #define INLINEFUNC_implies_nonnull_row 1 |
| 17142 | #define INLINEFUNC_expr_implies_expr 2 |
| 17143 | #define INLINEFUNC_expr_compare 3 |
| 17144 | #define INLINEFUNC_affinity 4 |
| 17145 | #define INLINEFUNC_unlikely 99 /* Default case */ |
| 17146 | |
| 17147 | /* |
| 17148 | ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are |
| 17149 | ** used to create the initializers for the FuncDef structures. |
| @@ -20064,12 +20065,14 @@ | |
| 20064 | # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) |
| 20065 | #endif |
| 20066 | SQLITE_PRIVATE int sqlite3ReadOnlyShadowTables(sqlite3 *db); |
| 20067 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 20068 | SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName); |
| 20069 | #else |
| 20070 | # define sqlite3ShadowTableName(A,B) 0 |
| 20071 | #endif |
| 20072 | SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse*,Module*); |
| 20073 | SQLITE_PRIVATE void sqlite3VtabEponymousTableClear(sqlite3*,Module*); |
| 20074 | SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse*,Table*); |
| 20075 | SQLITE_PRIVATE void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int); |
| @@ -27924,11 +27927,11 @@ | |
| 27924 | } |
| 27925 | }else{ |
| 27926 | assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); |
| 27927 | assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); |
| 27928 | sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
| 27929 | pNew = sqlite3_realloc64(p, n); |
| 27930 | if( !pNew ){ |
| 27931 | sqlite3OomFault(db); |
| 27932 | } |
| 27933 | sqlite3MemdebugSetType(pNew, |
| 27934 | (db->lookaside.bDisable==0 ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP)); |
| @@ -29001,11 +29004,11 @@ | |
| 29001 | p->nAlloc = (int)szNew; |
| 29002 | } |
| 29003 | if( p->db ){ |
| 29004 | zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc); |
| 29005 | }else{ |
| 29006 | zNew = sqlite3_realloc64(zOld, p->nAlloc); |
| 29007 | } |
| 29008 | if( zNew ){ |
| 29009 | assert( p->zText!=0 || p->nChar==0 ); |
| 29010 | if( !isMalloced(p) && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar); |
| 29011 | p->zText = zNew; |
| @@ -29343,11 +29346,11 @@ | |
| 29343 | ** and segfaults if you give it a long long int. |
| 29344 | */ |
| 29345 | SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){ |
| 29346 | va_list ap; |
| 29347 | StrAccum acc; |
| 29348 | char zBuf[500]; |
| 29349 | sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); |
| 29350 | va_start(ap,zFormat); |
| 29351 | sqlite3_str_vappendf(&acc, zFormat, ap); |
| 29352 | va_end(ap); |
| 29353 | sqlite3StrAccumFinish(&acc); |
| @@ -45048,10 +45051,11 @@ | |
| 45048 | } |
| 45049 | |
| 45050 | /* Forward references to VFS helper methods used for temporary files */ |
| 45051 | static int winGetTempname(sqlite3_vfs *, char **); |
| 45052 | static int winIsDir(const void *); |
| 45053 | static BOOL winIsDriveLetterAndColon(const char *); |
| 45054 | |
| 45055 | /* |
| 45056 | ** Control and query of the open file handle. |
| 45057 | */ |
| @@ -46817,11 +46821,13 @@ | |
| 46817 | pFile->pVfs = pVfs; |
| 46818 | pFile->h = h; |
| 46819 | if( isReadonly ){ |
| 46820 | pFile->ctrlFlags |= WINFILE_RDONLY; |
| 46821 | } |
| 46822 | if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){ |
| 46823 | pFile->ctrlFlags |= WINFILE_PSOW; |
| 46824 | } |
| 46825 | pFile->lastErrno = NO_ERROR; |
| 46826 | pFile->zPath = zName; |
| 46827 | #if SQLITE_MAX_MMAP_SIZE>0 |
| @@ -47026,10 +47032,21 @@ | |
| 47026 | *pResOut = rc; |
| 47027 | OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", |
| 47028 | zFilename, pResOut, *pResOut)); |
| 47029 | return SQLITE_OK; |
| 47030 | } |
| 47031 | |
| 47032 | /* |
| 47033 | ** Returns non-zero if the specified path name starts with a drive letter |
| 47034 | ** followed by a colon character. |
| 47035 | */ |
| @@ -47091,14 +47108,15 @@ | |
| 47091 | DWORD nByte; |
| 47092 | void *zConverted; |
| 47093 | char *zOut; |
| 47094 | #endif |
| 47095 | |
| 47096 | /* If this path name begins with "/X:", where "X" is any alphabetic |
| 47097 | ** character, discard the initial "/" from the pathname. |
| 47098 | */ |
| 47099 | if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){ |
| 47100 | zRelative++; |
| 47101 | } |
| 47102 | |
| 47103 | #if defined(__CYGWIN__) |
| 47104 | SimulateIOError( return SQLITE_ERROR ); |
| @@ -47850,11 +47868,11 @@ | |
| 47850 | if( newSz>p->szMax ){ |
| 47851 | return SQLITE_FULL; |
| 47852 | } |
| 47853 | newSz *= 2; |
| 47854 | if( newSz>p->szMax ) newSz = p->szMax; |
| 47855 | pNew = sqlite3_realloc64(p->aData, newSz); |
| 47856 | if( pNew==0 ) return SQLITE_NOMEM; |
| 47857 | p->aData = pNew; |
| 47858 | p->szAlloc = newSz; |
| 47859 | return SQLITE_OK; |
| 47860 | } |
| @@ -59820,11 +59838,11 @@ | |
| 59820 | |
| 59821 | /* Enlarge the pWal->apWiData[] array if required */ |
| 59822 | if( pWal->nWiData<=iPage ){ |
| 59823 | sqlite3_int64 nByte = sizeof(u32*)*(iPage+1); |
| 59824 | volatile u32 **apNew; |
| 59825 | apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte); |
| 59826 | if( !apNew ){ |
| 59827 | *ppPage = 0; |
| 59828 | return SQLITE_NOMEM_BKPT; |
| 59829 | } |
| 59830 | memset((void*)&apNew[pWal->nWiData], 0, |
| @@ -59941,29 +59959,47 @@ | |
| 59941 | |
| 59942 | aOut[0] = s1; |
| 59943 | aOut[1] = s2; |
| 59944 | } |
| 59945 | |
| 59946 | static void walShmBarrier(Wal *pWal){ |
| 59947 | if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){ |
| 59948 | sqlite3OsShmBarrier(pWal->pDbFd); |
| 59949 | } |
| 59950 | } |
| 59951 | |
| 59952 | /* |
| 59953 | ** Write the header information in pWal->hdr into the wal-index. |
| 59954 | ** |
| 59955 | ** The checksum on pWal->hdr is updated before it is written. |
| 59956 | */ |
| 59957 | static void walIndexWriteHdr(Wal *pWal){ |
| 59958 | volatile WalIndexHdr *aHdr = walIndexHdr(pWal); |
| 59959 | const int nCksum = offsetof(WalIndexHdr, aCksum); |
| 59960 | |
| 59961 | assert( pWal->writeLock ); |
| 59962 | pWal->hdr.isInit = 1; |
| 59963 | pWal->hdr.iVersion = WALINDEX_MAX_VERSION; |
| 59964 | walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum); |
| 59965 | memcpy((void*)&aHdr[1], (const void*)&pWal->hdr, sizeof(WalIndexHdr)); |
| 59966 | walShmBarrier(pWal); |
| 59967 | memcpy((void*)&aHdr[0], (const void*)&pWal->hdr, sizeof(WalIndexHdr)); |
| 59968 | } |
| 59969 | |
| @@ -61074,11 +61110,11 @@ | |
| 61074 | pWal->nCkpt++; |
| 61075 | pWal->hdr.mxFrame = 0; |
| 61076 | sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); |
| 61077 | memcpy(&pWal->hdr.aSalt[1], &salt1, 4); |
| 61078 | walIndexWriteHdr(pWal); |
| 61079 | pInfo->nBackfill = 0; |
| 61080 | pInfo->nBackfillAttempted = 0; |
| 61081 | pInfo->aReadMark[1] = 0; |
| 61082 | for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; |
| 61083 | assert( pInfo->aReadMark[0]==0 ); |
| 61084 | } |
| @@ -61149,36 +61185,17 @@ | |
| 61149 | ** cannot be backfilled from the WAL. |
| 61150 | */ |
| 61151 | mxSafeFrame = pWal->hdr.mxFrame; |
| 61152 | mxPage = pWal->hdr.nPage; |
| 61153 | for(i=1; i<WAL_NREADER; i++){ |
| 61154 | /* Thread-sanitizer reports that the following is an unsafe read, |
| 61155 | ** as some other thread may be in the process of updating the value |
| 61156 | ** of the aReadMark[] slot. The assumption here is that if that is |
| 61157 | ** happening, the other client may only be increasing the value, |
| 61158 | ** not decreasing it. So assuming either that either the "old" or |
| 61159 | ** "new" version of the value is read, and not some arbitrary value |
| 61160 | ** that would never be written by a real client, things are still |
| 61161 | ** safe. |
| 61162 | ** |
| 61163 | ** Astute readers have pointed out that the assumption stated in the |
| 61164 | ** last sentence of the previous paragraph is not guaranteed to be |
| 61165 | ** true for all conforming systems. However, the assumption is true |
| 61166 | ** for all compilers and architectures in common use today (circa |
| 61167 | ** 2019-11-27) and the alternatives are both slow and complex, and |
| 61168 | ** so we will continue to go with the current design for now. If this |
| 61169 | ** bothers you, or if you really are running on a system where aligned |
| 61170 | ** 32-bit reads and writes are not atomic, then you can simply avoid |
| 61171 | ** the use of WAL mode, or only use WAL mode together with |
| 61172 | ** PRAGMA locking_mode=EXCLUSIVE and all will be well. |
| 61173 | */ |
| 61174 | u32 y = pInfo->aReadMark[i]; |
| 61175 | if( mxSafeFrame>y ){ |
| 61176 | assert( y<=pWal->hdr.mxFrame ); |
| 61177 | rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); |
| 61178 | if( rc==SQLITE_OK ){ |
| 61179 | pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED); |
| 61180 | walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 61181 | }else if( rc==SQLITE_BUSY ){ |
| 61182 | mxSafeFrame = y; |
| 61183 | xBusy = 0; |
| 61184 | }else{ |
| @@ -61192,11 +61209,11 @@ | |
| 61192 | rc = walIteratorInit(pWal, pInfo->nBackfill, &pIter); |
| 61193 | assert( rc==SQLITE_OK || pIter==0 ); |
| 61194 | } |
| 61195 | |
| 61196 | if( pIter |
| 61197 | && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK |
| 61198 | ){ |
| 61199 | u32 nBackfill = pInfo->nBackfill; |
| 61200 | |
| 61201 | pInfo->nBackfillAttempted = mxSafeFrame; |
| 61202 | |
| @@ -61248,11 +61265,11 @@ | |
| 61248 | if( rc==SQLITE_OK ){ |
| 61249 | rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags)); |
| 61250 | } |
| 61251 | } |
| 61252 | if( rc==SQLITE_OK ){ |
| 61253 | pInfo->nBackfill = mxSafeFrame; |
| 61254 | } |
| 61255 | } |
| 61256 | |
| 61257 | /* Release the reader lock held while backfilling */ |
| 61258 | walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); |
| @@ -61407,11 +61424,11 @@ | |
| 61407 | ** and *pChanged is set to 1. |
| 61408 | ** |
| 61409 | ** If the checksum cannot be verified return non-zero. If the header |
| 61410 | ** is read successfully and the checksum verified, return zero. |
| 61411 | */ |
| 61412 | static int walIndexTryHdr(Wal *pWal, int *pChanged){ |
| 61413 | u32 aCksum[2]; /* Checksum on the header content */ |
| 61414 | WalIndexHdr h1, h2; /* Two copies of the header content */ |
| 61415 | WalIndexHdr volatile *aHdr; /* Header in shared memory */ |
| 61416 | |
| 61417 | /* The first page of the wal-index must be mapped at this point. */ |
| @@ -61420,17 +61437,23 @@ | |
| 61420 | /* Read the header. This might happen concurrently with a write to the |
| 61421 | ** same area of shared memory on a different CPU in a SMP, |
| 61422 | ** meaning it is possible that an inconsistent snapshot is read |
| 61423 | ** from the file. If this happens, return non-zero. |
| 61424 | ** |
| 61425 | ** There are two copies of the header at the beginning of the wal-index. |
| 61426 | ** When reading, read [0] first then [1]. Writes are in the reverse order. |
| 61427 | ** Memory barriers are used to prevent the compiler or the hardware from |
| 61428 | ** reordering the reads and writes. |
| 61429 | */ |
| 61430 | aHdr = walIndexHdr(pWal); |
| 61431 | memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); |
| 61432 | walShmBarrier(pWal); |
| 61433 | memcpy(&h2, (void *)&aHdr[1], sizeof(h2)); |
| 61434 | |
| 61435 | if( memcmp(&h1, &h2, sizeof(h1))!=0 ){ |
| 61436 | return 1; /* Dirty read */ |
| @@ -61871,11 +61894,11 @@ | |
| 61871 | } |
| 61872 | |
| 61873 | assert( pWal->nWiData>0 ); |
| 61874 | assert( pWal->apWiData[0]!=0 ); |
| 61875 | pInfo = walCkptInfo(pWal); |
| 61876 | if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame |
| 61877 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 61878 | && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0) |
| 61879 | #endif |
| 61880 | ){ |
| 61881 | /* The WAL has been completely backfilled (or it is empty). |
| @@ -62038,11 +62061,11 @@ | |
| 62038 | void *pBuf2 = sqlite3_malloc(szPage); |
| 62039 | if( pBuf1==0 || pBuf2==0 ){ |
| 62040 | rc = SQLITE_NOMEM; |
| 62041 | }else{ |
| 62042 | u32 i = pInfo->nBackfillAttempted; |
| 62043 | for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){ |
| 62044 | WalHashLoc sLoc; /* Hash table location */ |
| 62045 | u32 pgno; /* Page number in db file */ |
| 62046 | i64 iDbOff; /* Offset of db file entry */ |
| 62047 | i64 iWalOff; /* Offset of wal file entry */ |
| 62048 | |
| @@ -62267,26 +62290,28 @@ | |
| 62267 | for(iHash=walFramePage(iLast); iHash>=iMinHash; iHash--){ |
| 62268 | WalHashLoc sLoc; /* Hash table location */ |
| 62269 | int iKey; /* Hash slot index */ |
| 62270 | int nCollide; /* Number of hash collisions remaining */ |
| 62271 | int rc; /* Error code */ |
| 62272 | |
| 62273 | rc = walHashGet(pWal, iHash, &sLoc); |
| 62274 | if( rc!=SQLITE_OK ){ |
| 62275 | return rc; |
| 62276 | } |
| 62277 | nCollide = HASHTABLE_NSLOT; |
| 62278 | for(iKey=walHash(pgno); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){ |
| 62279 | u32 iH = sLoc.aHash[iKey]; |
| 62280 | u32 iFrame = iH + sLoc.iZero; |
| 62281 | if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH]==pgno ){ |
| 62282 | assert( iFrame>iRead || CORRUPT_DB ); |
| 62283 | iRead = iFrame; |
| 62284 | } |
| 62285 | if( (nCollide--)==0 ){ |
| 62286 | return SQLITE_CORRUPT_BKPT; |
| 62287 | } |
| 62288 | } |
| 62289 | if( iRead ) break; |
| 62290 | } |
| 62291 | |
| 62292 | #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT |
| @@ -79307,10 +79332,11 @@ | |
| 79307 | char *zP4; |
| 79308 | char *zCom; |
| 79309 | sqlite3 dummyDb; |
| 79310 | static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n"; |
| 79311 | if( pOut==0 ) pOut = stdout; |
| 79312 | dummyDb.mallocFailed = 1; |
| 79313 | zP4 = sqlite3VdbeDisplayP4(&dummyDb, pOp); |
| 79314 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 79315 | zCom = sqlite3VdbeDisplayComment(0, pOp, zP4); |
| 79316 | #else |
| @@ -79325,10 +79351,11 @@ | |
| 79325 | zCom ? zCom : "" |
| 79326 | ); |
| 79327 | fflush(pOut); |
| 79328 | sqlite3_free(zP4); |
| 79329 | sqlite3_free(zCom); |
| 79330 | } |
| 79331 | #endif |
| 79332 | |
| 79333 | /* |
| 79334 | ** Initialize an array of N Mem element. |
| @@ -84066,11 +84093,11 @@ | |
| 84066 | p->db->errCode = SQLITE_OK; |
| 84067 | |
| 84068 | /* If the bit corresponding to this variable in Vdbe.expmask is set, then |
| 84069 | ** binding a new value to this variable invalidates the current query plan. |
| 84070 | ** |
| 84071 | ** IMPLEMENTATION-OF: R-48440-37595 If the specific value bound to host |
| 84072 | ** parameter in the WHERE clause might influence the choice of query plan |
| 84073 | ** for a statement, then the statement will be automatically recompiled, |
| 84074 | ** as if there had been a schema change, on the first sqlite3_step() call |
| 84075 | ** following any change to the bindings of that parameter. |
| 84076 | */ |
| @@ -96365,12 +96392,12 @@ | |
| 96365 | ************************************************************************* |
| 96366 | ** |
| 96367 | ** This file implements virtual-tables for examining the bytecode content |
| 96368 | ** of a prepared statement. |
| 96369 | */ |
| 96370 | #ifdef SQLITE_ENABLE_BYTECODE_VTAB |
| 96371 | /* #include "sqliteInt.h" */ |
| 96372 | /* #include "vdbeInt.h" */ |
| 96373 | |
| 96374 | /* An instance of the bytecode() table-valued function. |
| 96375 | */ |
| 96376 | typedef struct bytecodevtab bytecodevtab; |
| @@ -96771,10 +96798,12 @@ | |
| 96771 | if( rc==SQLITE_OK ){ |
| 96772 | rc = sqlite3_create_module(db, "tables_used", &bytecodevtabModule, &db); |
| 96773 | } |
| 96774 | return rc; |
| 96775 | } |
| 96776 | #endif /* SQLITE_ENABLE_BYTECODE_VTAB */ |
| 96777 | |
| 96778 | /************** End of vdbevtab.c ********************************************/ |
| 96779 | /************** Begin file memjournal.c **************************************/ |
| 96780 | /* |
| @@ -103075,10 +103104,17 @@ | |
| 103075 | } |
| 103076 | setDoNotMergeFlagOnCopy(v); |
| 103077 | sqlite3VdbeResolveLabel(v, endCoalesce); |
| 103078 | break; |
| 103079 | } |
| 103080 | |
| 103081 | default: { |
| 103082 | /* The UNLIKELY() function is a no-op. The result is the value |
| 103083 | ** of the first argument. |
| 103084 | */ |
| @@ -105497,11 +105533,14 @@ | |
| 105497 | if( !zName ) goto exit_rename_table; |
| 105498 | |
| 105499 | /* Check that a table or index named 'zName' does not already exist |
| 105500 | ** in database iDb. If so, this is an error. |
| 105501 | */ |
| 105502 | if( sqlite3FindTable(db, zName, zDb) || sqlite3FindIndex(db, zName, zDb) ){ |
| 105503 | sqlite3ErrorMsg(pParse, |
| 105504 | "there is already another table or index with this name: %s", zName); |
| 105505 | goto exit_rename_table; |
| 105506 | } |
| 105507 | |
| @@ -110244,26 +110283,43 @@ | |
| 110244 | ** exists */ |
| 110245 | if( db->auth.authLevel<UAUTH_Admin && sqlite3UserAuthTable(zName)!=0 ){ |
| 110246 | return 0; |
| 110247 | } |
| 110248 | #endif |
| 110249 | while(1){ |
| 110250 | for(i=OMIT_TEMPDB; i<db->nDb; i++){ |
| 110251 | int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ |
| 110252 | if( zDatabase==0 || sqlite3DbIsNamed(db, j, zDatabase) ){ |
| 110253 | assert( sqlite3SchemaMutexHeld(db, j, 0) ); |
| 110254 | p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName); |
| 110255 | if( p ) return p; |
| 110256 | } |
| 110257 | } |
| 110258 | /* Not found. If the name we were looking for was temp.sqlite_master |
| 110259 | ** then change the name to sqlite_temp_master and try again. */ |
| 110260 | if( sqlite3StrICmp(zName, MASTER_NAME)!=0 ) break; |
| 110261 | if( sqlite3_stricmp(zDatabase, db->aDb[1].zDbSName)!=0 ) break; |
| 110262 | zName = TEMP_MASTER_NAME; |
| 110263 | } |
| 110264 | return 0; |
| 110265 | } |
| 110266 | |
| 110267 | /* |
| 110268 | ** Locate the in-memory structure that describes a particular database |
| 110269 | ** table given the name of that table and (optionally) the name of the |
| @@ -112063,10 +112119,32 @@ | |
| 112063 | assert( pPk->nColumn==j ); |
| 112064 | assert( pTab->nNVCol<=j ); |
| 112065 | recomputeColumnsNotIndexed(pPk); |
| 112066 | } |
| 112067 | |
| 112068 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 112069 | /* |
| 112070 | ** Return true if zName is a shadow table name in the current database |
| 112071 | ** connection. |
| 112072 | ** |
| @@ -112074,26 +112152,21 @@ | |
| 112074 | ** restored to its original value prior to this routine returning. |
| 112075 | */ |
| 112076 | SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName){ |
| 112077 | char *zTail; /* Pointer to the last "_" in zName */ |
| 112078 | Table *pTab; /* Table that zName is a shadow of */ |
| 112079 | Module *pMod; /* Module for the virtual table */ |
| 112080 | |
| 112081 | zTail = strrchr(zName, '_'); |
| 112082 | if( zTail==0 ) return 0; |
| 112083 | *zTail = 0; |
| 112084 | pTab = sqlite3FindTable(db, zName, 0); |
| 112085 | *zTail = '_'; |
| 112086 | if( pTab==0 ) return 0; |
| 112087 | if( !IsVirtual(pTab) ) return 0; |
| 112088 | pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]); |
| 112089 | if( pMod==0 ) return 0; |
| 112090 | if( pMod->pModule->iVersion<3 ) return 0; |
| 112091 | if( pMod->pModule->xShadowName==0 ) return 0; |
| 112092 | return pMod->pModule->xShadowName(zTail+1); |
| 112093 | } |
| 112094 | #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 112095 | |
| 112096 | #ifdef SQLITE_DEBUG |
| 112097 | /* |
| 112098 | ** Mark all nodes of an expression as EP_Immutable, indicating that |
| 112099 | ** they should not be changed. Expressions attached to a table or |
| @@ -117780,11 +117853,11 @@ | |
| 117780 | if( (cntExpand&(cntExpand-1))==0 ){ |
| 117781 | /* Grow the size of the output buffer only on substitutions |
| 117782 | ** whose index is a power of two: 1, 2, 4, 8, 16, 32, ... */ |
| 117783 | u8 *zOld; |
| 117784 | zOld = zOut; |
| 117785 | zOut = sqlite3_realloc64(zOut, (int)nOut + (nOut - nStr - 1)); |
| 117786 | if( zOut==0 ){ |
| 117787 | sqlite3_result_error_nomem(context); |
| 117788 | sqlite3_free(zOld); |
| 117789 | return; |
| 117790 | } |
| @@ -118477,11 +118550,11 @@ | |
| 118477 | FUNCTION(round, 2, 0, 0, roundFunc ), |
| 118478 | #endif |
| 118479 | FUNCTION(upper, 1, 0, 0, upperFunc ), |
| 118480 | FUNCTION(lower, 1, 0, 0, lowerFunc ), |
| 118481 | FUNCTION(hex, 1, 0, 0, hexFunc ), |
| 118482 | INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, SQLITE_FUNC_COALESCE), |
| 118483 | VFUNCTION(random, 0, 0, 0, randomFunc ), |
| 118484 | VFUNCTION(randomblob, 1, 0, 0, randomBlob ), |
| 118485 | FUNCTION(nullif, 2, 0, 1, nullifFunc ), |
| 118486 | DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ), |
| 118487 | DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), |
| @@ -118517,11 +118590,12 @@ | |
| 118517 | #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION |
| 118518 | FUNCTION(unknown, -1, 0, 0, unknownFunc ), |
| 118519 | #endif |
| 118520 | FUNCTION(coalesce, 1, 0, 0, 0 ), |
| 118521 | FUNCTION(coalesce, 0, 0, 0, 0 ), |
| 118522 | INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, SQLITE_FUNC_COALESCE), |
| 118523 | }; |
| 118524 | #ifndef SQLITE_OMIT_ALTERTABLE |
| 118525 | sqlite3AlterFunctions(); |
| 118526 | #endif |
| 118527 | sqlite3WindowFunctions(); |
| @@ -135570,11 +135644,11 @@ | |
| 135570 | need = nCol; |
| 135571 | } |
| 135572 | if( p->nData + need > p->nAlloc ){ |
| 135573 | char **azNew; |
| 135574 | p->nAlloc = p->nAlloc*2 + need; |
| 135575 | azNew = sqlite3_realloc64( p->azResult, sizeof(char*)*p->nAlloc ); |
| 135576 | if( azNew==0 ) goto malloc_failed; |
| 135577 | p->azResult = azNew; |
| 135578 | } |
| 135579 | |
| 135580 | /* If this is the first row, then generate an extra row containing |
| @@ -135679,11 +135753,11 @@ | |
| 135679 | sqlite3_free_table(&res.azResult[1]); |
| 135680 | return rc; |
| 135681 | } |
| 135682 | if( res.nAlloc>res.nData ){ |
| 135683 | char **azNew; |
| 135684 | azNew = sqlite3_realloc64( res.azResult, sizeof(char*)*res.nData ); |
| 135685 | if( azNew==0 ){ |
| 135686 | sqlite3_free_table(&res.azResult[1]); |
| 135687 | db->errCode = SQLITE_NOMEM; |
| 135688 | return SQLITE_NOMEM_BKPT; |
| 135689 | } |
| @@ -136969,14 +137043,14 @@ | |
| 136969 | ** Therefore, the P4 parameter is only required if the default value for |
| 136970 | ** the column is a literal number, string or null. The sqlite3ValueFromExpr() |
| 136971 | ** function is capable of transforming these types of expressions into |
| 136972 | ** sqlite3_value objects. |
| 136973 | ** |
| 136974 | ** If parameter iReg is not negative, code an OP_RealAffinity instruction |
| 136975 | ** on register iReg. This is used when an equivalent integer value is |
| 136976 | ** stored in place of an 8-byte floating point value in order to save |
| 136977 | ** space. |
| 136978 | */ |
| 136979 | SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ |
| 136980 | assert( pTab!=0 ); |
| 136981 | if( !pTab->pSelect ){ |
| 136982 | sqlite3_value *pValue = 0; |
| @@ -136989,11 +137063,11 @@ | |
| 136989 | if( pValue ){ |
| 136990 | sqlite3VdbeAppendP4(v, pValue, P4_MEM); |
| 136991 | } |
| 136992 | } |
| 136993 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 136994 | if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ |
| 136995 | sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); |
| 136996 | } |
| 136997 | #endif |
| 136998 | } |
| 136999 | |
| @@ -139838,11 +139912,11 @@ | |
| 139838 | assert( IsVirtual(pTab) ); |
| 139839 | for(i=0; i<pToplevel->nVtabLock; i++){ |
| 139840 | if( pTab==pToplevel->apVtabLock[i] ) return; |
| 139841 | } |
| 139842 | n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]); |
| 139843 | apVtabLock = sqlite3_realloc64(pToplevel->apVtabLock, n); |
| 139844 | if( apVtabLock ){ |
| 139845 | pToplevel->apVtabLock = apVtabLock; |
| 139846 | pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab; |
| 139847 | }else{ |
| 139848 | sqlite3OomFault(pToplevel->db); |
| @@ -151130,17 +151204,23 @@ | |
| 151130 | ){ |
| 151131 | if( pAppend ){ |
| 151132 | int i; |
| 151133 | int nInit = pList ? pList->nExpr : 0; |
| 151134 | for(i=0; i<pAppend->nExpr; i++){ |
| 151135 | int iDummy; |
| 151136 | Expr *pDup = sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0); |
| 151137 | assert( pDup==0 || !ExprHasProperty(pDup, EP_MemToken) ); |
| 151138 | if( bIntToNull && pDup && sqlite3ExprIsInteger(pDup, &iDummy) ){ |
| 151139 | pDup->op = TK_NULL; |
| 151140 | pDup->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse); |
| 151141 | pDup->u.zToken = 0; |
| 151142 | } |
| 151143 | pList = sqlite3ExprListAppend(pParse, pList, pDup); |
| 151144 | if( pList ) pList->a[nInit+i].sortFlags = pAppend->a[i].sortFlags; |
| 151145 | } |
| 151146 | } |
| @@ -160322,10 +160402,11 @@ | |
| 160322 | } |
| 160323 | #endif |
| 160324 | if( rc==SQLITE_OK ){ |
| 160325 | sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, |
| 160326 | sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); |
| 160327 | sqlite3GlobalConfig.isInit = 1; |
| 160328 | #ifdef SQLITE_EXTRA_INIT |
| 160329 | bRunExtraInit = 1; |
| 160330 | #endif |
| 160331 | } |
| @@ -163270,11 +163351,11 @@ | |
| 163270 | assert( SQLITE_OPEN_CREATE == 0x04 ); |
| 163271 | testcase( (1<<(flags&7))==0x02 ); /* READONLY */ |
| 163272 | testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ |
| 163273 | testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ |
| 163274 | if( ((1<<(flags&7)) & 0x46)==0 ){ |
| 163275 | rc = SQLITE_MISUSE_BKPT; /* IMP: R-65497-44594 */ |
| 163276 | }else{ |
| 163277 | rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); |
| 163278 | } |
| 163279 | if( rc!=SQLITE_OK ){ |
| 163280 | if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); |
| @@ -171194,10 +171275,11 @@ | |
| 171194 | while( *pRc==SQLITE_OK && pLeft->bEof==0 ){ |
| 171195 | memset(pDl->pList, 0, pDl->nList); |
| 171196 | fts3EvalNextRow(pCsr, pLeft, pRc); |
| 171197 | } |
| 171198 | } |
| 171199 | } |
| 171200 | } |
| 171201 | break; |
| 171202 | } |
| 171203 | |
| @@ -182739,11 +182821,11 @@ | |
| 182739 | iStart = pExpr->iPhrase * p->nCol; |
| 182740 | }else{ |
| 182741 | iStart = pExpr->iPhrase * ((p->nCol + 31) / 32); |
| 182742 | } |
| 182743 | |
| 182744 | while( 1 ){ |
| 182745 | int nHit = fts3ColumnlistCount(&pIter); |
| 182746 | if( (pPhrase->iColumn>=pTab->nColumn || pPhrase->iColumn==iCol) ){ |
| 182747 | if( p->flag==FTS3_MATCHINFO_LHITS ){ |
| 182748 | p->aMatchinfo[iStart + iCol] = (u32)nHit; |
| 182749 | }else if( nHit ){ |
| @@ -184653,10 +184735,11 @@ | |
| 184653 | } |
| 184654 | |
| 184655 | /* Append N bytes from zIn onto the end of the JsonString string. |
| 184656 | */ |
| 184657 | static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ |
| 184658 | if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return; |
| 184659 | memcpy(p->zBuf+p->nUsed, zIn, N); |
| 184660 | p->nUsed += N; |
| 184661 | } |
| 184662 | |
| @@ -224664,11 +224747,11 @@ | |
| 224664 | int nArg, /* Number of args */ |
| 224665 | sqlite3_value **apUnused /* Function arguments */ |
| 224666 | ){ |
| 224667 | assert( nArg==0 ); |
| 224668 | UNUSED_PARAM2(nArg, apUnused); |
| 224669 | sqlite3_result_text(pCtx, "fts5: 2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff", -1, SQLITE_TRANSIENT); |
| 224670 | } |
| 224671 | |
| 224672 | /* |
| 224673 | ** Return true if zName is the extension on one of the shadow tables used |
| 224674 | ** by this module. |
| @@ -229447,12 +229530,12 @@ | |
| 229447 | } |
| 229448 | #endif /* SQLITE_CORE */ |
| 229449 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 229450 | |
| 229451 | /************** End of stmt.c ************************************************/ |
| 229452 | #if __LINE__!=229452 |
| 229453 | #undef SQLITE_SOURCE_ID |
| 229454 | #define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f96alt2" |
| 229455 | #endif |
| 229456 | /* Return the source-id for this library */ |
| 229457 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 229458 | /************************** End of sqlite3.c ******************************/ |
| 229459 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1162,11 +1162,11 @@ | |
| 1162 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1163 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1164 | */ |
| 1165 | #define SQLITE_VERSION "3.32.0" |
| 1166 | #define SQLITE_VERSION_NUMBER 3032000 |
| 1167 | #define SQLITE_SOURCE_ID "2020-05-19 15:51:10 3117c1b5a9e348fd8d16ba9d03fdafaad8514567fb3403f72b86d6162ad40bde" |
| 1168 | |
| 1169 | /* |
| 1170 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1171 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1172 | ** |
| @@ -6532,11 +6532,11 @@ | |
| 6532 | ** when first called if N is less than or equal to zero or if a memory |
| 6533 | ** allocate error occurs. |
| 6534 | ** |
| 6535 | ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is |
| 6536 | ** determined by the N parameter on first successful call. Changing the |
| 6537 | ** value of N in any subsequent call to sqlite3_aggregate_context() within |
| 6538 | ** the same aggregate function instance will not resize the memory |
| 6539 | ** allocation.)^ Within the xFinal callback, it is customary to set |
| 6540 | ** N=0 in calls to sqlite3_aggregate_context(C,N) so that no |
| 6541 | ** pointless memory allocations occur. |
| 6542 | ** |
| @@ -17119,11 +17119,11 @@ | |
| 17119 | #define SQLITE_FUNC_EPHEM 0x0010 /* Ephemeral. Delete with VDBE */ |
| 17120 | #define SQLITE_FUNC_NEEDCOLL 0x0020 /* sqlite3GetFuncCollSeq() might be called*/ |
| 17121 | #define SQLITE_FUNC_LENGTH 0x0040 /* Built-in length() function */ |
| 17122 | #define SQLITE_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */ |
| 17123 | #define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */ |
| 17124 | /* 0x0200 -- available for reuse */ |
| 17125 | #define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */ |
| 17126 | #define SQLITE_FUNC_CONSTANT 0x0800 /* Constant inputs give a constant output */ |
| 17127 | #define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */ |
| 17128 | #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a |
| 17129 | ** single query - might change over time */ |
| @@ -17140,10 +17140,11 @@ | |
| 17140 | #define INLINEFUNC_coalesce 0 |
| 17141 | #define INLINEFUNC_implies_nonnull_row 1 |
| 17142 | #define INLINEFUNC_expr_implies_expr 2 |
| 17143 | #define INLINEFUNC_expr_compare 3 |
| 17144 | #define INLINEFUNC_affinity 4 |
| 17145 | #define INLINEFUNC_iif 5 |
| 17146 | #define INLINEFUNC_unlikely 99 /* Default case */ |
| 17147 | |
| 17148 | /* |
| 17149 | ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are |
| 17150 | ** used to create the initializers for the FuncDef structures. |
| @@ -20064,12 +20065,14 @@ | |
| 20065 | # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) |
| 20066 | #endif |
| 20067 | SQLITE_PRIVATE int sqlite3ReadOnlyShadowTables(sqlite3 *db); |
| 20068 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 20069 | SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName); |
| 20070 | SQLITE_PRIVATE int sqlite3IsShadowTableOf(sqlite3*,Table*,const char*); |
| 20071 | #else |
| 20072 | # define sqlite3ShadowTableName(A,B) 0 |
| 20073 | # define sqlite3IsShadowTableOf(A,B,C) 0 |
| 20074 | #endif |
| 20075 | SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse*,Module*); |
| 20076 | SQLITE_PRIVATE void sqlite3VtabEponymousTableClear(sqlite3*,Module*); |
| 20077 | SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse*,Table*); |
| 20078 | SQLITE_PRIVATE void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int); |
| @@ -27924,11 +27927,11 @@ | |
| 27927 | } |
| 27928 | }else{ |
| 27929 | assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); |
| 27930 | assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); |
| 27931 | sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
| 27932 | pNew = sqlite3Realloc(p, n); |
| 27933 | if( !pNew ){ |
| 27934 | sqlite3OomFault(db); |
| 27935 | } |
| 27936 | sqlite3MemdebugSetType(pNew, |
| 27937 | (db->lookaside.bDisable==0 ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP)); |
| @@ -29001,11 +29004,11 @@ | |
| 29004 | p->nAlloc = (int)szNew; |
| 29005 | } |
| 29006 | if( p->db ){ |
| 29007 | zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc); |
| 29008 | }else{ |
| 29009 | zNew = sqlite3Realloc(zOld, p->nAlloc); |
| 29010 | } |
| 29011 | if( zNew ){ |
| 29012 | assert( p->zText!=0 || p->nChar==0 ); |
| 29013 | if( !isMalloced(p) && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar); |
| 29014 | p->zText = zNew; |
| @@ -29343,11 +29346,11 @@ | |
| 29346 | ** and segfaults if you give it a long long int. |
| 29347 | */ |
| 29348 | SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){ |
| 29349 | va_list ap; |
| 29350 | StrAccum acc; |
| 29351 | char zBuf[SQLITE_PRINT_BUF_SIZE*10]; |
| 29352 | sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); |
| 29353 | va_start(ap,zFormat); |
| 29354 | sqlite3_str_vappendf(&acc, zFormat, ap); |
| 29355 | va_end(ap); |
| 29356 | sqlite3StrAccumFinish(&acc); |
| @@ -45048,10 +45051,11 @@ | |
| 45051 | } |
| 45052 | |
| 45053 | /* Forward references to VFS helper methods used for temporary files */ |
| 45054 | static int winGetTempname(sqlite3_vfs *, char **); |
| 45055 | static int winIsDir(const void *); |
| 45056 | static BOOL winIsLongPathPrefix(const char *); |
| 45057 | static BOOL winIsDriveLetterAndColon(const char *); |
| 45058 | |
| 45059 | /* |
| 45060 | ** Control and query of the open file handle. |
| 45061 | */ |
| @@ -46817,11 +46821,13 @@ | |
| 46821 | pFile->pVfs = pVfs; |
| 46822 | pFile->h = h; |
| 46823 | if( isReadonly ){ |
| 46824 | pFile->ctrlFlags |= WINFILE_RDONLY; |
| 46825 | } |
| 46826 | if( (flags & SQLITE_OPEN_MAIN_DB) |
| 46827 | && sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) |
| 46828 | ){ |
| 46829 | pFile->ctrlFlags |= WINFILE_PSOW; |
| 46830 | } |
| 46831 | pFile->lastErrno = NO_ERROR; |
| 46832 | pFile->zPath = zName; |
| 46833 | #if SQLITE_MAX_MMAP_SIZE>0 |
| @@ -47026,10 +47032,21 @@ | |
| 47032 | *pResOut = rc; |
| 47033 | OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", |
| 47034 | zFilename, pResOut, *pResOut)); |
| 47035 | return SQLITE_OK; |
| 47036 | } |
| 47037 | |
| 47038 | /* |
| 47039 | ** Returns non-zero if the specified path name starts with the "long path" |
| 47040 | ** prefix. |
| 47041 | */ |
| 47042 | static BOOL winIsLongPathPrefix( |
| 47043 | const char *zPathname |
| 47044 | ){ |
| 47045 | return ( zPathname[0]=='\\' && zPathname[1]=='\\' |
| 47046 | && zPathname[2]=='?' && zPathname[3]=='\\' ); |
| 47047 | } |
| 47048 | |
| 47049 | /* |
| 47050 | ** Returns non-zero if the specified path name starts with a drive letter |
| 47051 | ** followed by a colon character. |
| 47052 | */ |
| @@ -47091,14 +47108,15 @@ | |
| 47108 | DWORD nByte; |
| 47109 | void *zConverted; |
| 47110 | char *zOut; |
| 47111 | #endif |
| 47112 | |
| 47113 | /* If this path name begins with "/X:" or "\\?\", where "X" is any |
| 47114 | ** alphabetic character, discard the initial "/" from the pathname. |
| 47115 | */ |
| 47116 | if( zRelative[0]=='/' && (winIsDriveLetterAndColon(zRelative+1) |
| 47117 | || winIsLongPathPrefix(zRelative+1)) ){ |
| 47118 | zRelative++; |
| 47119 | } |
| 47120 | |
| 47121 | #if defined(__CYGWIN__) |
| 47122 | SimulateIOError( return SQLITE_ERROR ); |
| @@ -47850,11 +47868,11 @@ | |
| 47868 | if( newSz>p->szMax ){ |
| 47869 | return SQLITE_FULL; |
| 47870 | } |
| 47871 | newSz *= 2; |
| 47872 | if( newSz>p->szMax ) newSz = p->szMax; |
| 47873 | pNew = sqlite3Realloc(p->aData, newSz); |
| 47874 | if( pNew==0 ) return SQLITE_NOMEM; |
| 47875 | p->aData = pNew; |
| 47876 | p->szAlloc = newSz; |
| 47877 | return SQLITE_OK; |
| 47878 | } |
| @@ -59820,11 +59838,11 @@ | |
| 59838 | |
| 59839 | /* Enlarge the pWal->apWiData[] array if required */ |
| 59840 | if( pWal->nWiData<=iPage ){ |
| 59841 | sqlite3_int64 nByte = sizeof(u32*)*(iPage+1); |
| 59842 | volatile u32 **apNew; |
| 59843 | apNew = (volatile u32 **)sqlite3Realloc((void *)pWal->apWiData, nByte); |
| 59844 | if( !apNew ){ |
| 59845 | *ppPage = 0; |
| 59846 | return SQLITE_NOMEM_BKPT; |
| 59847 | } |
| 59848 | memset((void*)&apNew[pWal->nWiData], 0, |
| @@ -59941,29 +59959,47 @@ | |
| 59959 | |
| 59960 | aOut[0] = s1; |
| 59961 | aOut[1] = s2; |
| 59962 | } |
| 59963 | |
| 59964 | /* |
| 59965 | ** If there is the possibility of concurrent access to the SHM file |
| 59966 | ** from multiple threads and/or processes, then do a memory barrier. |
| 59967 | */ |
| 59968 | static void walShmBarrier(Wal *pWal){ |
| 59969 | if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){ |
| 59970 | sqlite3OsShmBarrier(pWal->pDbFd); |
| 59971 | } |
| 59972 | } |
| 59973 | |
| 59974 | /* |
| 59975 | ** Add the SQLITE_NO_TSAN as part of the return-type of a function |
| 59976 | ** definition as a hint that the function contains constructs that |
| 59977 | ** might give false-positive TSAN warnings. |
| 59978 | ** |
| 59979 | ** See tag-20200519-1. |
| 59980 | */ |
| 59981 | #if defined(__clang__) && !defined(SQLITE_NO_TSAN) |
| 59982 | # define SQLITE_NO_TSAN __attribute__((no_sanitize_thread)) |
| 59983 | #else |
| 59984 | # define SQLITE_NO_TSAN |
| 59985 | #endif |
| 59986 | |
| 59987 | /* |
| 59988 | ** Write the header information in pWal->hdr into the wal-index. |
| 59989 | ** |
| 59990 | ** The checksum on pWal->hdr is updated before it is written. |
| 59991 | */ |
| 59992 | static SQLITE_NO_TSAN void walIndexWriteHdr(Wal *pWal){ |
| 59993 | volatile WalIndexHdr *aHdr = walIndexHdr(pWal); |
| 59994 | const int nCksum = offsetof(WalIndexHdr, aCksum); |
| 59995 | |
| 59996 | assert( pWal->writeLock ); |
| 59997 | pWal->hdr.isInit = 1; |
| 59998 | pWal->hdr.iVersion = WALINDEX_MAX_VERSION; |
| 59999 | walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum); |
| 60000 | /* Possible TSAN false-positive. See tag-20200519-1 */ |
| 60001 | memcpy((void*)&aHdr[1], (const void*)&pWal->hdr, sizeof(WalIndexHdr)); |
| 60002 | walShmBarrier(pWal); |
| 60003 | memcpy((void*)&aHdr[0], (const void*)&pWal->hdr, sizeof(WalIndexHdr)); |
| 60004 | } |
| 60005 | |
| @@ -61074,11 +61110,11 @@ | |
| 61110 | pWal->nCkpt++; |
| 61111 | pWal->hdr.mxFrame = 0; |
| 61112 | sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); |
| 61113 | memcpy(&pWal->hdr.aSalt[1], &salt1, 4); |
| 61114 | walIndexWriteHdr(pWal); |
| 61115 | AtomicStore(&pInfo->nBackfill, 0); |
| 61116 | pInfo->nBackfillAttempted = 0; |
| 61117 | pInfo->aReadMark[1] = 0; |
| 61118 | for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; |
| 61119 | assert( pInfo->aReadMark[0]==0 ); |
| 61120 | } |
| @@ -61149,36 +61185,17 @@ | |
| 61185 | ** cannot be backfilled from the WAL. |
| 61186 | */ |
| 61187 | mxSafeFrame = pWal->hdr.mxFrame; |
| 61188 | mxPage = pWal->hdr.nPage; |
| 61189 | for(i=1; i<WAL_NREADER; i++){ |
| 61190 | u32 y = AtomicLoad(pInfo->aReadMark+i); |
| 61191 | if( mxSafeFrame>y ){ |
| 61192 | assert( y<=pWal->hdr.mxFrame ); |
| 61193 | rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); |
| 61194 | if( rc==SQLITE_OK ){ |
| 61195 | u32 iMark = (i==1 ? mxSafeFrame : READMARK_NOT_USED); |
| 61196 | AtomicStore(pInfo->aReadMark+i, iMark); |
| 61197 | walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 61198 | }else if( rc==SQLITE_BUSY ){ |
| 61199 | mxSafeFrame = y; |
| 61200 | xBusy = 0; |
| 61201 | }else{ |
| @@ -61192,11 +61209,11 @@ | |
| 61209 | rc = walIteratorInit(pWal, pInfo->nBackfill, &pIter); |
| 61210 | assert( rc==SQLITE_OK || pIter==0 ); |
| 61211 | } |
| 61212 | |
| 61213 | if( pIter |
| 61214 | && (rc = walBusyLock(pWal,xBusy,pBusyArg,WAL_READ_LOCK(0),1))==SQLITE_OK |
| 61215 | ){ |
| 61216 | u32 nBackfill = pInfo->nBackfill; |
| 61217 | |
| 61218 | pInfo->nBackfillAttempted = mxSafeFrame; |
| 61219 | |
| @@ -61248,11 +61265,11 @@ | |
| 61265 | if( rc==SQLITE_OK ){ |
| 61266 | rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags)); |
| 61267 | } |
| 61268 | } |
| 61269 | if( rc==SQLITE_OK ){ |
| 61270 | AtomicStore(&pInfo->nBackfill, mxSafeFrame); |
| 61271 | } |
| 61272 | } |
| 61273 | |
| 61274 | /* Release the reader lock held while backfilling */ |
| 61275 | walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); |
| @@ -61407,11 +61424,11 @@ | |
| 61424 | ** and *pChanged is set to 1. |
| 61425 | ** |
| 61426 | ** If the checksum cannot be verified return non-zero. If the header |
| 61427 | ** is read successfully and the checksum verified, return zero. |
| 61428 | */ |
| 61429 | static SQLITE_NO_TSAN int walIndexTryHdr(Wal *pWal, int *pChanged){ |
| 61430 | u32 aCksum[2]; /* Checksum on the header content */ |
| 61431 | WalIndexHdr h1, h2; /* Two copies of the header content */ |
| 61432 | WalIndexHdr volatile *aHdr; /* Header in shared memory */ |
| 61433 | |
| 61434 | /* The first page of the wal-index must be mapped at this point. */ |
| @@ -61420,17 +61437,23 @@ | |
| 61437 | /* Read the header. This might happen concurrently with a write to the |
| 61438 | ** same area of shared memory on a different CPU in a SMP, |
| 61439 | ** meaning it is possible that an inconsistent snapshot is read |
| 61440 | ** from the file. If this happens, return non-zero. |
| 61441 | ** |
| 61442 | ** tag-20200519-1: |
| 61443 | ** There are two copies of the header at the beginning of the wal-index. |
| 61444 | ** When reading, read [0] first then [1]. Writes are in the reverse order. |
| 61445 | ** Memory barriers are used to prevent the compiler or the hardware from |
| 61446 | ** reordering the reads and writes. TSAN and similar tools can sometimes |
| 61447 | ** give false-positive warnings about these accesses because the tools do not |
| 61448 | ** account for the double-read and the memory barrier. The use of mutexes |
| 61449 | ** here would be problematic as the memory being accessed is potentially |
| 61450 | ** shared among multiple processes and not all mutex implementions work |
| 61451 | ** reliably in that environment. |
| 61452 | */ |
| 61453 | aHdr = walIndexHdr(pWal); |
| 61454 | memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); /* Possible TSAN false-positive */ |
| 61455 | walShmBarrier(pWal); |
| 61456 | memcpy(&h2, (void *)&aHdr[1], sizeof(h2)); |
| 61457 | |
| 61458 | if( memcmp(&h1, &h2, sizeof(h1))!=0 ){ |
| 61459 | return 1; /* Dirty read */ |
| @@ -61871,11 +61894,11 @@ | |
| 61894 | } |
| 61895 | |
| 61896 | assert( pWal->nWiData>0 ); |
| 61897 | assert( pWal->apWiData[0]!=0 ); |
| 61898 | pInfo = walCkptInfo(pWal); |
| 61899 | if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame |
| 61900 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 61901 | && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0) |
| 61902 | #endif |
| 61903 | ){ |
| 61904 | /* The WAL has been completely backfilled (or it is empty). |
| @@ -62038,11 +62061,11 @@ | |
| 62061 | void *pBuf2 = sqlite3_malloc(szPage); |
| 62062 | if( pBuf1==0 || pBuf2==0 ){ |
| 62063 | rc = SQLITE_NOMEM; |
| 62064 | }else{ |
| 62065 | u32 i = pInfo->nBackfillAttempted; |
| 62066 | for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){ |
| 62067 | WalHashLoc sLoc; /* Hash table location */ |
| 62068 | u32 pgno; /* Page number in db file */ |
| 62069 | i64 iDbOff; /* Offset of db file entry */ |
| 62070 | i64 iWalOff; /* Offset of wal file entry */ |
| 62071 | |
| @@ -62267,26 +62290,28 @@ | |
| 62290 | for(iHash=walFramePage(iLast); iHash>=iMinHash; iHash--){ |
| 62291 | WalHashLoc sLoc; /* Hash table location */ |
| 62292 | int iKey; /* Hash slot index */ |
| 62293 | int nCollide; /* Number of hash collisions remaining */ |
| 62294 | int rc; /* Error code */ |
| 62295 | u32 iH; |
| 62296 | |
| 62297 | rc = walHashGet(pWal, iHash, &sLoc); |
| 62298 | if( rc!=SQLITE_OK ){ |
| 62299 | return rc; |
| 62300 | } |
| 62301 | nCollide = HASHTABLE_NSLOT; |
| 62302 | iKey = walHash(pgno); |
| 62303 | while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){ |
| 62304 | u32 iFrame = iH + sLoc.iZero; |
| 62305 | if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH]==pgno ){ |
| 62306 | assert( iFrame>iRead || CORRUPT_DB ); |
| 62307 | iRead = iFrame; |
| 62308 | } |
| 62309 | if( (nCollide--)==0 ){ |
| 62310 | return SQLITE_CORRUPT_BKPT; |
| 62311 | } |
| 62312 | iKey = walNextHash(iKey); |
| 62313 | } |
| 62314 | if( iRead ) break; |
| 62315 | } |
| 62316 | |
| 62317 | #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT |
| @@ -79307,10 +79332,11 @@ | |
| 79332 | char *zP4; |
| 79333 | char *zCom; |
| 79334 | sqlite3 dummyDb; |
| 79335 | static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n"; |
| 79336 | if( pOut==0 ) pOut = stdout; |
| 79337 | sqlite3BeginBenignMalloc(); |
| 79338 | dummyDb.mallocFailed = 1; |
| 79339 | zP4 = sqlite3VdbeDisplayP4(&dummyDb, pOp); |
| 79340 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 79341 | zCom = sqlite3VdbeDisplayComment(0, pOp, zP4); |
| 79342 | #else |
| @@ -79325,10 +79351,11 @@ | |
| 79351 | zCom ? zCom : "" |
| 79352 | ); |
| 79353 | fflush(pOut); |
| 79354 | sqlite3_free(zP4); |
| 79355 | sqlite3_free(zCom); |
| 79356 | sqlite3EndBenignMalloc(); |
| 79357 | } |
| 79358 | #endif |
| 79359 | |
| 79360 | /* |
| 79361 | ** Initialize an array of N Mem element. |
| @@ -84066,11 +84093,11 @@ | |
| 84093 | p->db->errCode = SQLITE_OK; |
| 84094 | |
| 84095 | /* If the bit corresponding to this variable in Vdbe.expmask is set, then |
| 84096 | ** binding a new value to this variable invalidates the current query plan. |
| 84097 | ** |
| 84098 | ** IMPLEMENTATION-OF: R-57496-20354 If the specific value bound to a host |
| 84099 | ** parameter in the WHERE clause might influence the choice of query plan |
| 84100 | ** for a statement, then the statement will be automatically recompiled, |
| 84101 | ** as if there had been a schema change, on the first sqlite3_step() call |
| 84102 | ** following any change to the bindings of that parameter. |
| 84103 | */ |
| @@ -96365,12 +96392,12 @@ | |
| 96392 | ************************************************************************* |
| 96393 | ** |
| 96394 | ** This file implements virtual-tables for examining the bytecode content |
| 96395 | ** of a prepared statement. |
| 96396 | */ |
| 96397 | /* #include "sqliteInt.h" */ |
| 96398 | #if defined(SQLITE_ENABLE_BYTECODE_VTAB) && !defined(SQLITE_OMIT_VIRTUALTABLE) |
| 96399 | /* #include "vdbeInt.h" */ |
| 96400 | |
| 96401 | /* An instance of the bytecode() table-valued function. |
| 96402 | */ |
| 96403 | typedef struct bytecodevtab bytecodevtab; |
| @@ -96771,10 +96798,12 @@ | |
| 96798 | if( rc==SQLITE_OK ){ |
| 96799 | rc = sqlite3_create_module(db, "tables_used", &bytecodevtabModule, &db); |
| 96800 | } |
| 96801 | return rc; |
| 96802 | } |
| 96803 | #elif defined(SQLITE_ENABLE_BYTECODE_VTAB) |
| 96804 | SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3 *db){ return SQLITE_OK; } |
| 96805 | #endif /* SQLITE_ENABLE_BYTECODE_VTAB */ |
| 96806 | |
| 96807 | /************** End of vdbevtab.c ********************************************/ |
| 96808 | /************** Begin file memjournal.c **************************************/ |
| 96809 | /* |
| @@ -103075,10 +103104,17 @@ | |
| 103104 | } |
| 103105 | setDoNotMergeFlagOnCopy(v); |
| 103106 | sqlite3VdbeResolveLabel(v, endCoalesce); |
| 103107 | break; |
| 103108 | } |
| 103109 | case INLINEFUNC_iif: { |
| 103110 | Expr caseExpr; |
| 103111 | memset(&caseExpr, 0, sizeof(caseExpr)); |
| 103112 | caseExpr.op = TK_CASE; |
| 103113 | caseExpr.x.pList = pFarg; |
| 103114 | return sqlite3ExprCodeTarget(pParse, &caseExpr, target); |
| 103115 | } |
| 103116 | |
| 103117 | default: { |
| 103118 | /* The UNLIKELY() function is a no-op. The result is the value |
| 103119 | ** of the first argument. |
| 103120 | */ |
| @@ -105497,11 +105533,14 @@ | |
| 105533 | if( !zName ) goto exit_rename_table; |
| 105534 | |
| 105535 | /* Check that a table or index named 'zName' does not already exist |
| 105536 | ** in database iDb. If so, this is an error. |
| 105537 | */ |
| 105538 | if( sqlite3FindTable(db, zName, zDb) |
| 105539 | || sqlite3FindIndex(db, zName, zDb) |
| 105540 | || sqlite3IsShadowTableOf(db, pTab, zName) |
| 105541 | ){ |
| 105542 | sqlite3ErrorMsg(pParse, |
| 105543 | "there is already another table or index with this name: %s", zName); |
| 105544 | goto exit_rename_table; |
| 105545 | } |
| 105546 | |
| @@ -110244,26 +110283,43 @@ | |
| 110283 | ** exists */ |
| 110284 | if( db->auth.authLevel<UAUTH_Admin && sqlite3UserAuthTable(zName)!=0 ){ |
| 110285 | return 0; |
| 110286 | } |
| 110287 | #endif |
| 110288 | if( zDatabase ){ |
| 110289 | for(i=0; i<db->nDb; i++){ |
| 110290 | if( sqlite3StrICmp(zDatabase, db->aDb[i].zDbSName)==0 ) break; |
| 110291 | } |
| 110292 | if( i>=db->nDb ){ |
| 110293 | /* No match against the official names. But always match "main" |
| 110294 | ** to schema 0 as a legacy fallback. */ |
| 110295 | if( sqlite3StrICmp(zDatabase,"main")==0 ){ |
| 110296 | i = 0; |
| 110297 | }else{ |
| 110298 | return 0; |
| 110299 | } |
| 110300 | } |
| 110301 | p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName); |
| 110302 | if( p==0 && i==1 && sqlite3StrICmp(zName, MASTER_NAME)==0 ){ |
| 110303 | /* All temp.sqlite_master to be an alias for sqlite_temp_master */ |
| 110304 | p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash, TEMP_MASTER_NAME); |
| 110305 | } |
| 110306 | }else{ |
| 110307 | /* Match against TEMP first */ |
| 110308 | p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash, zName); |
| 110309 | if( p ) return p; |
| 110310 | /* The main database is second */ |
| 110311 | p = sqlite3HashFind(&db->aDb[0].pSchema->tblHash, zName); |
| 110312 | if( p ) return p; |
| 110313 | /* Attached databases are in order of attachment */ |
| 110314 | for(i=2; i<db->nDb; i++){ |
| 110315 | assert( sqlite3SchemaMutexHeld(db, i, 0) ); |
| 110316 | p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName); |
| 110317 | if( p ) break; |
| 110318 | } |
| 110319 | } |
| 110320 | return p; |
| 110321 | } |
| 110322 | |
| 110323 | /* |
| 110324 | ** Locate the in-memory structure that describes a particular database |
| 110325 | ** table given the name of that table and (optionally) the name of the |
| @@ -112063,10 +112119,32 @@ | |
| 112119 | assert( pPk->nColumn==j ); |
| 112120 | assert( pTab->nNVCol<=j ); |
| 112121 | recomputeColumnsNotIndexed(pPk); |
| 112122 | } |
| 112123 | |
| 112124 | |
| 112125 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 112126 | /* |
| 112127 | ** Return true if pTab is a virtual table and zName is a shadow table name |
| 112128 | ** for that virtual table. |
| 112129 | */ |
| 112130 | SQLITE_PRIVATE int sqlite3IsShadowTableOf(sqlite3 *db, Table *pTab, const char *zName){ |
| 112131 | int nName; /* Length of zName */ |
| 112132 | Module *pMod; /* Module for the virtual table */ |
| 112133 | |
| 112134 | if( !IsVirtual(pTab) ) return 0; |
| 112135 | nName = sqlite3Strlen30(pTab->zName); |
| 112136 | if( sqlite3_strnicmp(zName, pTab->zName, nName)!=0 ) return 0; |
| 112137 | if( zName[nName]!='_' ) return 0; |
| 112138 | pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]); |
| 112139 | if( pMod==0 ) return 0; |
| 112140 | if( pMod->pModule->iVersion<3 ) return 0; |
| 112141 | if( pMod->pModule->xShadowName==0 ) return 0; |
| 112142 | return pMod->pModule->xShadowName(zName+nName+1); |
| 112143 | } |
| 112144 | #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 112145 | |
| 112146 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 112147 | /* |
| 112148 | ** Return true if zName is a shadow table name in the current database |
| 112149 | ** connection. |
| 112150 | ** |
| @@ -112074,26 +112152,21 @@ | |
| 112152 | ** restored to its original value prior to this routine returning. |
| 112153 | */ |
| 112154 | SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName){ |
| 112155 | char *zTail; /* Pointer to the last "_" in zName */ |
| 112156 | Table *pTab; /* Table that zName is a shadow of */ |
| 112157 | zTail = strrchr(zName, '_'); |
| 112158 | if( zTail==0 ) return 0; |
| 112159 | *zTail = 0; |
| 112160 | pTab = sqlite3FindTable(db, zName, 0); |
| 112161 | *zTail = '_'; |
| 112162 | if( pTab==0 ) return 0; |
| 112163 | if( !IsVirtual(pTab) ) return 0; |
| 112164 | return sqlite3IsShadowTableOf(db, pTab, zName); |
| 112165 | } |
| 112166 | #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 112167 | |
| 112168 | |
| 112169 | #ifdef SQLITE_DEBUG |
| 112170 | /* |
| 112171 | ** Mark all nodes of an expression as EP_Immutable, indicating that |
| 112172 | ** they should not be changed. Expressions attached to a table or |
| @@ -117780,11 +117853,11 @@ | |
| 117853 | if( (cntExpand&(cntExpand-1))==0 ){ |
| 117854 | /* Grow the size of the output buffer only on substitutions |
| 117855 | ** whose index is a power of two: 1, 2, 4, 8, 16, 32, ... */ |
| 117856 | u8 *zOld; |
| 117857 | zOld = zOut; |
| 117858 | zOut = sqlite3Realloc(zOut, (int)nOut + (nOut - nStr - 1)); |
| 117859 | if( zOut==0 ){ |
| 117860 | sqlite3_result_error_nomem(context); |
| 117861 | sqlite3_free(zOld); |
| 117862 | return; |
| 117863 | } |
| @@ -118477,11 +118550,11 @@ | |
| 118550 | FUNCTION(round, 2, 0, 0, roundFunc ), |
| 118551 | #endif |
| 118552 | FUNCTION(upper, 1, 0, 0, upperFunc ), |
| 118553 | FUNCTION(lower, 1, 0, 0, lowerFunc ), |
| 118554 | FUNCTION(hex, 1, 0, 0, hexFunc ), |
| 118555 | INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ), |
| 118556 | VFUNCTION(random, 0, 0, 0, randomFunc ), |
| 118557 | VFUNCTION(randomblob, 1, 0, 0, randomBlob ), |
| 118558 | FUNCTION(nullif, 2, 0, 1, nullifFunc ), |
| 118559 | DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ), |
| 118560 | DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), |
| @@ -118517,11 +118590,12 @@ | |
| 118590 | #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION |
| 118591 | FUNCTION(unknown, -1, 0, 0, unknownFunc ), |
| 118592 | #endif |
| 118593 | FUNCTION(coalesce, 1, 0, 0, 0 ), |
| 118594 | FUNCTION(coalesce, 0, 0, 0, 0 ), |
| 118595 | INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ), |
| 118596 | INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ), |
| 118597 | }; |
| 118598 | #ifndef SQLITE_OMIT_ALTERTABLE |
| 118599 | sqlite3AlterFunctions(); |
| 118600 | #endif |
| 118601 | sqlite3WindowFunctions(); |
| @@ -135570,11 +135644,11 @@ | |
| 135644 | need = nCol; |
| 135645 | } |
| 135646 | if( p->nData + need > p->nAlloc ){ |
| 135647 | char **azNew; |
| 135648 | p->nAlloc = p->nAlloc*2 + need; |
| 135649 | azNew = sqlite3Realloc( p->azResult, sizeof(char*)*p->nAlloc ); |
| 135650 | if( azNew==0 ) goto malloc_failed; |
| 135651 | p->azResult = azNew; |
| 135652 | } |
| 135653 | |
| 135654 | /* If this is the first row, then generate an extra row containing |
| @@ -135679,11 +135753,11 @@ | |
| 135753 | sqlite3_free_table(&res.azResult[1]); |
| 135754 | return rc; |
| 135755 | } |
| 135756 | if( res.nAlloc>res.nData ){ |
| 135757 | char **azNew; |
| 135758 | azNew = sqlite3Realloc( res.azResult, sizeof(char*)*res.nData ); |
| 135759 | if( azNew==0 ){ |
| 135760 | sqlite3_free_table(&res.azResult[1]); |
| 135761 | db->errCode = SQLITE_NOMEM; |
| 135762 | return SQLITE_NOMEM_BKPT; |
| 135763 | } |
| @@ -136969,14 +137043,14 @@ | |
| 137043 | ** Therefore, the P4 parameter is only required if the default value for |
| 137044 | ** the column is a literal number, string or null. The sqlite3ValueFromExpr() |
| 137045 | ** function is capable of transforming these types of expressions into |
| 137046 | ** sqlite3_value objects. |
| 137047 | ** |
| 137048 | ** If column as REAL affinity and the table is an ordinary b-tree table |
| 137049 | ** (not a virtual table) then the value might have been stored as an |
| 137050 | ** integer. In that case, add an OP_RealAffinity opcode to make sure |
| 137051 | ** it has been converted into REAL. |
| 137052 | */ |
| 137053 | SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ |
| 137054 | assert( pTab!=0 ); |
| 137055 | if( !pTab->pSelect ){ |
| 137056 | sqlite3_value *pValue = 0; |
| @@ -136989,11 +137063,11 @@ | |
| 137063 | if( pValue ){ |
| 137064 | sqlite3VdbeAppendP4(v, pValue, P4_MEM); |
| 137065 | } |
| 137066 | } |
| 137067 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 137068 | if( pTab->aCol[i].affinity==SQLITE_AFF_REAL && !IsVirtual(pTab) ){ |
| 137069 | sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); |
| 137070 | } |
| 137071 | #endif |
| 137072 | } |
| 137073 | |
| @@ -139838,11 +139912,11 @@ | |
| 139912 | assert( IsVirtual(pTab) ); |
| 139913 | for(i=0; i<pToplevel->nVtabLock; i++){ |
| 139914 | if( pTab==pToplevel->apVtabLock[i] ) return; |
| 139915 | } |
| 139916 | n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]); |
| 139917 | apVtabLock = sqlite3Realloc(pToplevel->apVtabLock, n); |
| 139918 | if( apVtabLock ){ |
| 139919 | pToplevel->apVtabLock = apVtabLock; |
| 139920 | pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab; |
| 139921 | }else{ |
| 139922 | sqlite3OomFault(pToplevel->db); |
| @@ -151130,17 +151204,23 @@ | |
| 151204 | ){ |
| 151205 | if( pAppend ){ |
| 151206 | int i; |
| 151207 | int nInit = pList ? pList->nExpr : 0; |
| 151208 | for(i=0; i<pAppend->nExpr; i++){ |
| 151209 | Expr *pDup = sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0); |
| 151210 | assert( pDup==0 || !ExprHasProperty(pDup, EP_MemToken) ); |
| 151211 | if( bIntToNull && pDup ){ |
| 151212 | int iDummy; |
| 151213 | Expr *pSub; |
| 151214 | for(pSub=pDup; ExprHasProperty(pSub, EP_Skip); pSub=pSub->pLeft){ |
| 151215 | assert( pSub ); |
| 151216 | } |
| 151217 | if( sqlite3ExprIsInteger(pSub, &iDummy) ){ |
| 151218 | pSub->op = TK_NULL; |
| 151219 | pSub->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse); |
| 151220 | pSub->u.zToken = 0; |
| 151221 | } |
| 151222 | } |
| 151223 | pList = sqlite3ExprListAppend(pParse, pList, pDup); |
| 151224 | if( pList ) pList->a[nInit+i].sortFlags = pAppend->a[i].sortFlags; |
| 151225 | } |
| 151226 | } |
| @@ -160322,10 +160402,11 @@ | |
| 160402 | } |
| 160403 | #endif |
| 160404 | if( rc==SQLITE_OK ){ |
| 160405 | sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, |
| 160406 | sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); |
| 160407 | sqlite3MemoryBarrier(); |
| 160408 | sqlite3GlobalConfig.isInit = 1; |
| 160409 | #ifdef SQLITE_EXTRA_INIT |
| 160410 | bRunExtraInit = 1; |
| 160411 | #endif |
| 160412 | } |
| @@ -163270,11 +163351,11 @@ | |
| 163351 | assert( SQLITE_OPEN_CREATE == 0x04 ); |
| 163352 | testcase( (1<<(flags&7))==0x02 ); /* READONLY */ |
| 163353 | testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ |
| 163354 | testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ |
| 163355 | if( ((1<<(flags&7)) & 0x46)==0 ){ |
| 163356 | rc = SQLITE_MISUSE_BKPT; /* IMP: R-18321-05872 */ |
| 163357 | }else{ |
| 163358 | rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); |
| 163359 | } |
| 163360 | if( rc!=SQLITE_OK ){ |
| 163361 | if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); |
| @@ -171194,10 +171275,11 @@ | |
| 171275 | while( *pRc==SQLITE_OK && pLeft->bEof==0 ){ |
| 171276 | memset(pDl->pList, 0, pDl->nList); |
| 171277 | fts3EvalNextRow(pCsr, pLeft, pRc); |
| 171278 | } |
| 171279 | } |
| 171280 | pRight->bEof = pLeft->bEof = 1; |
| 171281 | } |
| 171282 | } |
| 171283 | break; |
| 171284 | } |
| 171285 | |
| @@ -182739,11 +182821,11 @@ | |
| 182821 | iStart = pExpr->iPhrase * p->nCol; |
| 182822 | }else{ |
| 182823 | iStart = pExpr->iPhrase * ((p->nCol + 31) / 32); |
| 182824 | } |
| 182825 | |
| 182826 | if( pIter ) while( 1 ){ |
| 182827 | int nHit = fts3ColumnlistCount(&pIter); |
| 182828 | if( (pPhrase->iColumn>=pTab->nColumn || pPhrase->iColumn==iCol) ){ |
| 182829 | if( p->flag==FTS3_MATCHINFO_LHITS ){ |
| 182830 | p->aMatchinfo[iStart + iCol] = (u32)nHit; |
| 182831 | }else if( nHit ){ |
| @@ -184653,10 +184735,11 @@ | |
| 184735 | } |
| 184736 | |
| 184737 | /* Append N bytes from zIn onto the end of the JsonString string. |
| 184738 | */ |
| 184739 | static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ |
| 184740 | if( N==0 ) return; |
| 184741 | if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return; |
| 184742 | memcpy(p->zBuf+p->nUsed, zIn, N); |
| 184743 | p->nUsed += N; |
| 184744 | } |
| 184745 | |
| @@ -224664,11 +224747,11 @@ | |
| 224747 | int nArg, /* Number of args */ |
| 224748 | sqlite3_value **apUnused /* Function arguments */ |
| 224749 | ){ |
| 224750 | assert( nArg==0 ); |
| 224751 | UNUSED_PARAM2(nArg, apUnused); |
| 224752 | sqlite3_result_text(pCtx, "fts5: 2020-05-19 15:51:10 3117c1b5a9e348fd8d16ba9d03fdafaad8514567fb3403f72b86d6162ad40bde", -1, SQLITE_TRANSIENT); |
| 224753 | } |
| 224754 | |
| 224755 | /* |
| 224756 | ** Return true if zName is the extension on one of the shadow tables used |
| 224757 | ** by this module. |
| @@ -229447,12 +229530,12 @@ | |
| 229530 | } |
| 229531 | #endif /* SQLITE_CORE */ |
| 229532 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 229533 | |
| 229534 | /************** End of stmt.c ************************************************/ |
| 229535 | #if __LINE__!=229535 |
| 229536 | #undef SQLITE_SOURCE_ID |
| 229537 | #define SQLITE_SOURCE_ID "2020-05-19 15:51:10 3117c1b5a9e348fd8d16ba9d03fdafaad8514567fb3403f72b86d6162ad4alt2" |
| 229538 | #endif |
| 229539 | /* Return the source-id for this library */ |
| 229540 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 229541 | /************************** End of sqlite3.c ******************************/ |
| 229542 |
+2
-2
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -123,11 +123,11 @@ | ||
| 123 | 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | 125 | */ |
| 126 | 126 | #define SQLITE_VERSION "3.32.0" |
| 127 | 127 | #define SQLITE_VERSION_NUMBER 3032000 |
| 128 | -#define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff" | |
| 128 | +#define SQLITE_SOURCE_ID "2020-05-19 15:51:10 3117c1b5a9e348fd8d16ba9d03fdafaad8514567fb3403f72b86d6162ad40bde" | |
| 129 | 129 | |
| 130 | 130 | /* |
| 131 | 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | 133 | ** |
| @@ -5493,11 +5493,11 @@ | ||
| 5493 | 5493 | ** when first called if N is less than or equal to zero or if a memory |
| 5494 | 5494 | ** allocate error occurs. |
| 5495 | 5495 | ** |
| 5496 | 5496 | ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is |
| 5497 | 5497 | ** determined by the N parameter on first successful call. Changing the |
| 5498 | -** value of N in any subsequents call to sqlite3_aggregate_context() within | |
| 5498 | +** value of N in any subsequent call to sqlite3_aggregate_context() within | |
| 5499 | 5499 | ** the same aggregate function instance will not resize the memory |
| 5500 | 5500 | ** allocation.)^ Within the xFinal callback, it is customary to set |
| 5501 | 5501 | ** N=0 in calls to sqlite3_aggregate_context(C,N) so that no |
| 5502 | 5502 | ** pointless memory allocations occur. |
| 5503 | 5503 | ** |
| 5504 | 5504 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -123,11 +123,11 @@ | |
| 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | */ |
| 126 | #define SQLITE_VERSION "3.32.0" |
| 127 | #define SQLITE_VERSION_NUMBER 3032000 |
| 128 | #define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| @@ -5493,11 +5493,11 @@ | |
| 5493 | ** when first called if N is less than or equal to zero or if a memory |
| 5494 | ** allocate error occurs. |
| 5495 | ** |
| 5496 | ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is |
| 5497 | ** determined by the N parameter on first successful call. Changing the |
| 5498 | ** value of N in any subsequents call to sqlite3_aggregate_context() within |
| 5499 | ** the same aggregate function instance will not resize the memory |
| 5500 | ** allocation.)^ Within the xFinal callback, it is customary to set |
| 5501 | ** N=0 in calls to sqlite3_aggregate_context(C,N) so that no |
| 5502 | ** pointless memory allocations occur. |
| 5503 | ** |
| 5504 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -123,11 +123,11 @@ | |
| 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | */ |
| 126 | #define SQLITE_VERSION "3.32.0" |
| 127 | #define SQLITE_VERSION_NUMBER 3032000 |
| 128 | #define SQLITE_SOURCE_ID "2020-05-19 15:51:10 3117c1b5a9e348fd8d16ba9d03fdafaad8514567fb3403f72b86d6162ad40bde" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| @@ -5493,11 +5493,11 @@ | |
| 5493 | ** when first called if N is less than or equal to zero or if a memory |
| 5494 | ** allocate error occurs. |
| 5495 | ** |
| 5496 | ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is |
| 5497 | ** determined by the N parameter on first successful call. Changing the |
| 5498 | ** value of N in any subsequent call to sqlite3_aggregate_context() within |
| 5499 | ** the same aggregate function instance will not resize the memory |
| 5500 | ** allocation.)^ Within the xFinal callback, it is customary to set |
| 5501 | ** N=0 in calls to sqlite3_aggregate_context(C,N) so that no |
| 5502 | ** pointless memory allocations occur. |
| 5503 | ** |
| 5504 |
+1
-1
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -1500,11 +1500,11 @@ | ||
| 1500 | 1500 | if( argc!=3 ){ |
| 1501 | 1501 | return Th_WrongNumArgs(interp, "unversioned content FILENAME"); |
| 1502 | 1502 | } |
| 1503 | 1503 | if( Th_IsRepositoryOpen() ){ |
| 1504 | 1504 | Blob content; |
| 1505 | - if( unversioned_content(argv[2], &content)==0 ){ | |
| 1505 | + if( unversioned_content(argv[2], &content)!=0 ){ | |
| 1506 | 1506 | Th_SetResult(interp, blob_str(&content), blob_size(&content)); |
| 1507 | 1507 | blob_reset(&content); |
| 1508 | 1508 | return TH_OK; |
| 1509 | 1509 | }else{ |
| 1510 | 1510 | return TH_ERROR; |
| 1511 | 1511 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -1500,11 +1500,11 @@ | |
| 1500 | if( argc!=3 ){ |
| 1501 | return Th_WrongNumArgs(interp, "unversioned content FILENAME"); |
| 1502 | } |
| 1503 | if( Th_IsRepositoryOpen() ){ |
| 1504 | Blob content; |
| 1505 | if( unversioned_content(argv[2], &content)==0 ){ |
| 1506 | Th_SetResult(interp, blob_str(&content), blob_size(&content)); |
| 1507 | blob_reset(&content); |
| 1508 | return TH_OK; |
| 1509 | }else{ |
| 1510 | return TH_ERROR; |
| 1511 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -1500,11 +1500,11 @@ | |
| 1500 | if( argc!=3 ){ |
| 1501 | return Th_WrongNumArgs(interp, "unversioned content FILENAME"); |
| 1502 | } |
| 1503 | if( Th_IsRepositoryOpen() ){ |
| 1504 | Blob content; |
| 1505 | if( unversioned_content(argv[2], &content)!=0 ){ |
| 1506 | Th_SetResult(interp, blob_str(&content), blob_size(&content)); |
| 1507 | blob_reset(&content); |
| 1508 | return TH_OK; |
| 1509 | }else{ |
| 1510 | return TH_ERROR; |
| 1511 |
+11
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1796,10 +1796,11 @@ | ||
| 1796 | 1796 | }else if( fossil_stricmp(zMatchStyle, "regexp")==0 ){ |
| 1797 | 1797 | matchStyle = MS_REGEXP; |
| 1798 | 1798 | }else{ |
| 1799 | 1799 | /* For exact maching, inhibit links to the selected tag. */ |
| 1800 | 1800 | zThisTag = zTagName; |
| 1801 | + Th_Store("current_checkin", zTagName); | |
| 1801 | 1802 | } |
| 1802 | 1803 | |
| 1803 | 1804 | /* Display a checkbox to enable/disable display of related check-ins. */ |
| 1804 | 1805 | if( advancedMenu ){ |
| 1805 | 1806 | style_submenu_checkbox("rel", "Related", 0, 0); |
| @@ -2201,10 +2202,17 @@ | ||
| 2201 | 2202 | "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);" |
| 2202 | 2203 | "INSERT OR IGNORE INTO selected_nodes" |
| 2203 | 2204 | " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag" |
| 2204 | 2205 | " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/ |
| 2205 | 2206 | ); |
| 2207 | + if( zMark ){ | |
| 2208 | + /* If the t=release option is used with m=UUID, then also | |
| 2209 | + ** include the UUID check-in in the display list */ | |
| 2210 | + int ridMark = name_to_rid(zMark); | |
| 2211 | + db_multi_exec( | |
| 2212 | + "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark); | |
| 2213 | + } | |
| 2206 | 2214 | if( !related ){ |
| 2207 | 2215 | blob_append_sql(&cond, " AND blob.rid IN selected_nodes"); |
| 2208 | 2216 | }else{ |
| 2209 | 2217 | db_multi_exec( |
| 2210 | 2218 | "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);" |
| @@ -2427,10 +2435,13 @@ | ||
| 2427 | 2435 | blob_appendf(&desc, " related to tags matching %h", zMatchDesc); |
| 2428 | 2436 | }else{ |
| 2429 | 2437 | blob_appendf(&desc, " with tags matching %h", zMatchDesc); |
| 2430 | 2438 | } |
| 2431 | 2439 | } |
| 2440 | + if( zMark ){ | |
| 2441 | + blob_appendf(&desc," plus check-in \"%h\"", zMark); | |
| 2442 | + } | |
| 2432 | 2443 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2433 | 2444 | } |
| 2434 | 2445 | addFileGlobDescription(zChng, &desc); |
| 2435 | 2446 | if( rAfter>0.0 ){ |
| 2436 | 2447 | if( rBefore>0.0 ){ |
| 2437 | 2448 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1796,10 +1796,11 @@ | |
| 1796 | }else if( fossil_stricmp(zMatchStyle, "regexp")==0 ){ |
| 1797 | matchStyle = MS_REGEXP; |
| 1798 | }else{ |
| 1799 | /* For exact maching, inhibit links to the selected tag. */ |
| 1800 | zThisTag = zTagName; |
| 1801 | } |
| 1802 | |
| 1803 | /* Display a checkbox to enable/disable display of related check-ins. */ |
| 1804 | if( advancedMenu ){ |
| 1805 | style_submenu_checkbox("rel", "Related", 0, 0); |
| @@ -2201,10 +2202,17 @@ | |
| 2201 | "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);" |
| 2202 | "INSERT OR IGNORE INTO selected_nodes" |
| 2203 | " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag" |
| 2204 | " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/ |
| 2205 | ); |
| 2206 | if( !related ){ |
| 2207 | blob_append_sql(&cond, " AND blob.rid IN selected_nodes"); |
| 2208 | }else{ |
| 2209 | db_multi_exec( |
| 2210 | "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);" |
| @@ -2427,10 +2435,13 @@ | |
| 2427 | blob_appendf(&desc, " related to tags matching %h", zMatchDesc); |
| 2428 | }else{ |
| 2429 | blob_appendf(&desc, " with tags matching %h", zMatchDesc); |
| 2430 | } |
| 2431 | } |
| 2432 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2433 | } |
| 2434 | addFileGlobDescription(zChng, &desc); |
| 2435 | if( rAfter>0.0 ){ |
| 2436 | if( rBefore>0.0 ){ |
| 2437 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1796,10 +1796,11 @@ | |
| 1796 | }else if( fossil_stricmp(zMatchStyle, "regexp")==0 ){ |
| 1797 | matchStyle = MS_REGEXP; |
| 1798 | }else{ |
| 1799 | /* For exact maching, inhibit links to the selected tag. */ |
| 1800 | zThisTag = zTagName; |
| 1801 | Th_Store("current_checkin", zTagName); |
| 1802 | } |
| 1803 | |
| 1804 | /* Display a checkbox to enable/disable display of related check-ins. */ |
| 1805 | if( advancedMenu ){ |
| 1806 | style_submenu_checkbox("rel", "Related", 0, 0); |
| @@ -2201,10 +2202,17 @@ | |
| 2202 | "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);" |
| 2203 | "INSERT OR IGNORE INTO selected_nodes" |
| 2204 | " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag" |
| 2205 | " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/ |
| 2206 | ); |
| 2207 | if( zMark ){ |
| 2208 | /* If the t=release option is used with m=UUID, then also |
| 2209 | ** include the UUID check-in in the display list */ |
| 2210 | int ridMark = name_to_rid(zMark); |
| 2211 | db_multi_exec( |
| 2212 | "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark); |
| 2213 | } |
| 2214 | if( !related ){ |
| 2215 | blob_append_sql(&cond, " AND blob.rid IN selected_nodes"); |
| 2216 | }else{ |
| 2217 | db_multi_exec( |
| 2218 | "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);" |
| @@ -2427,10 +2435,13 @@ | |
| 2435 | blob_appendf(&desc, " related to tags matching %h", zMatchDesc); |
| 2436 | }else{ |
| 2437 | blob_appendf(&desc, " with tags matching %h", zMatchDesc); |
| 2438 | } |
| 2439 | } |
| 2440 | if( zMark ){ |
| 2441 | blob_appendf(&desc," plus check-in \"%h\"", zMark); |
| 2442 | } |
| 2443 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2444 | } |
| 2445 | addFileGlobDescription(zChng, &desc); |
| 2446 | if( rAfter>0.0 ){ |
| 2447 | if( rBefore>0.0 ){ |
| 2448 |
+22
-7
| --- src/unversioned.c | ||
| +++ src/unversioned.c | ||
| @@ -85,25 +85,40 @@ | ||
| 85 | 85 | } |
| 86 | 86 | |
| 87 | 87 | /* |
| 88 | 88 | ** Initialize pContent to be the content of an unversioned file zName. |
| 89 | 89 | ** |
| 90 | -** Return 0 on success. Return 1 if zName is not found. | |
| 90 | +** Return 0 on failures. | |
| 91 | +** Return 1 if the file is found by name. | |
| 92 | +** Return 2 if the file is found by hash. | |
| 91 | 93 | */ |
| 92 | 94 | int unversioned_content(const char *zName, Blob *pContent){ |
| 93 | 95 | Stmt q; |
| 94 | - int rc = 1; | |
| 96 | + int rc = 0; | |
| 95 | 97 | blob_init(pContent, 0, 0); |
| 96 | - db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE name=%Q", zName); | |
| 98 | + db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE name=%Q", | |
| 99 | + zName); | |
| 97 | 100 | if( db_step(&q)==SQLITE_ROW ){ |
| 98 | 101 | db_column_blob(&q, 1, pContent); |
| 99 | 102 | if( db_column_int(&q, 0)==1 ){ |
| 100 | 103 | blob_uncompress(pContent, pContent); |
| 101 | 104 | } |
| 102 | - rc = 0; | |
| 105 | + rc = 1; | |
| 103 | 106 | } |
| 104 | 107 | db_finalize(&q); |
| 108 | + if( rc==0 && validate16(zName,-1) ){ | |
| 109 | + db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE hash=%Q", | |
| 110 | + zName); | |
| 111 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 112 | + db_column_blob(&q, 1, pContent); | |
| 113 | + if( db_column_int(&q, 0)==1 ){ | |
| 114 | + blob_uncompress(pContent, pContent); | |
| 115 | + } | |
| 116 | + rc = 2; | |
| 117 | + } | |
| 118 | + db_finalize(&q); | |
| 119 | + } | |
| 105 | 120 | return rc; |
| 106 | 121 | } |
| 107 | 122 | |
| 108 | 123 | /* |
| 109 | 124 | ** Write unversioned content into the database. |
| @@ -328,11 +343,11 @@ | ||
| 328 | 343 | int i; |
| 329 | 344 | verify_all_options(); |
| 330 | 345 | db_begin_transaction(); |
| 331 | 346 | for(i=3; i<g.argc; i++){ |
| 332 | 347 | Blob content; |
| 333 | - if( unversioned_content(g.argv[i], &content)==0 ){ | |
| 348 | + if( unversioned_content(g.argv[i], &content)!=0 ){ | |
| 334 | 349 | blob_write_to_file(&content, "-"); |
| 335 | 350 | } |
| 336 | 351 | blob_reset(&content); |
| 337 | 352 | } |
| 338 | 353 | db_end_transaction(0); |
| @@ -352,11 +367,11 @@ | ||
| 352 | 367 | } |
| 353 | 368 | zTFile = fossil_temp_filename(); |
| 354 | 369 | if( zTFile==0 ) fossil_fatal("cannot find a temporary filename"); |
| 355 | 370 | db_begin_transaction(); |
| 356 | 371 | content_rcvid_init("#!fossil unversioned edit"); |
| 357 | - if( unversioned_content(zUVFile, &content) ){ | |
| 372 | + if( unversioned_content(zUVFile, &content)==0 ){ | |
| 358 | 373 | fossil_fatal("no such uv-file: %Q", zUVFile); |
| 359 | 374 | } |
| 360 | 375 | if( looks_like_binary(&content) ){ |
| 361 | 376 | fossil_fatal("cannot edit binary content"); |
| 362 | 377 | } |
| @@ -381,11 +396,11 @@ | ||
| 381 | 396 | blob_reset(&content); |
| 382 | 397 | }else if( memcmp(zCmd, "export", nCmd)==0 ){ |
| 383 | 398 | Blob content; |
| 384 | 399 | verify_all_options(); |
| 385 | 400 | if( g.argc!=5 ) usage("export UVFILE OUTPUT"); |
| 386 | - if( unversioned_content(g.argv[3], &content) ){ | |
| 401 | + if( unversioned_content(g.argv[3], &content)==0 ){ | |
| 387 | 402 | fossil_fatal("no such uv-file: %Q", g.argv[3]); |
| 388 | 403 | } |
| 389 | 404 | blob_write_to_file(&content, g.argv[4]); |
| 390 | 405 | blob_reset(&content); |
| 391 | 406 | }else if( memcmp(zCmd, "hash", nCmd)==0 ){ /* undocumented */ |
| 392 | 407 |
| --- src/unversioned.c | |
| +++ src/unversioned.c | |
| @@ -85,25 +85,40 @@ | |
| 85 | } |
| 86 | |
| 87 | /* |
| 88 | ** Initialize pContent to be the content of an unversioned file zName. |
| 89 | ** |
| 90 | ** Return 0 on success. Return 1 if zName is not found. |
| 91 | */ |
| 92 | int unversioned_content(const char *zName, Blob *pContent){ |
| 93 | Stmt q; |
| 94 | int rc = 1; |
| 95 | blob_init(pContent, 0, 0); |
| 96 | db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE name=%Q", zName); |
| 97 | if( db_step(&q)==SQLITE_ROW ){ |
| 98 | db_column_blob(&q, 1, pContent); |
| 99 | if( db_column_int(&q, 0)==1 ){ |
| 100 | blob_uncompress(pContent, pContent); |
| 101 | } |
| 102 | rc = 0; |
| 103 | } |
| 104 | db_finalize(&q); |
| 105 | return rc; |
| 106 | } |
| 107 | |
| 108 | /* |
| 109 | ** Write unversioned content into the database. |
| @@ -328,11 +343,11 @@ | |
| 328 | int i; |
| 329 | verify_all_options(); |
| 330 | db_begin_transaction(); |
| 331 | for(i=3; i<g.argc; i++){ |
| 332 | Blob content; |
| 333 | if( unversioned_content(g.argv[i], &content)==0 ){ |
| 334 | blob_write_to_file(&content, "-"); |
| 335 | } |
| 336 | blob_reset(&content); |
| 337 | } |
| 338 | db_end_transaction(0); |
| @@ -352,11 +367,11 @@ | |
| 352 | } |
| 353 | zTFile = fossil_temp_filename(); |
| 354 | if( zTFile==0 ) fossil_fatal("cannot find a temporary filename"); |
| 355 | db_begin_transaction(); |
| 356 | content_rcvid_init("#!fossil unversioned edit"); |
| 357 | if( unversioned_content(zUVFile, &content) ){ |
| 358 | fossil_fatal("no such uv-file: %Q", zUVFile); |
| 359 | } |
| 360 | if( looks_like_binary(&content) ){ |
| 361 | fossil_fatal("cannot edit binary content"); |
| 362 | } |
| @@ -381,11 +396,11 @@ | |
| 381 | blob_reset(&content); |
| 382 | }else if( memcmp(zCmd, "export", nCmd)==0 ){ |
| 383 | Blob content; |
| 384 | verify_all_options(); |
| 385 | if( g.argc!=5 ) usage("export UVFILE OUTPUT"); |
| 386 | if( unversioned_content(g.argv[3], &content) ){ |
| 387 | fossil_fatal("no such uv-file: %Q", g.argv[3]); |
| 388 | } |
| 389 | blob_write_to_file(&content, g.argv[4]); |
| 390 | blob_reset(&content); |
| 391 | }else if( memcmp(zCmd, "hash", nCmd)==0 ){ /* undocumented */ |
| 392 |
| --- src/unversioned.c | |
| +++ src/unversioned.c | |
| @@ -85,25 +85,40 @@ | |
| 85 | } |
| 86 | |
| 87 | /* |
| 88 | ** Initialize pContent to be the content of an unversioned file zName. |
| 89 | ** |
| 90 | ** Return 0 on failures. |
| 91 | ** Return 1 if the file is found by name. |
| 92 | ** Return 2 if the file is found by hash. |
| 93 | */ |
| 94 | int unversioned_content(const char *zName, Blob *pContent){ |
| 95 | Stmt q; |
| 96 | int rc = 0; |
| 97 | blob_init(pContent, 0, 0); |
| 98 | db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE name=%Q", |
| 99 | zName); |
| 100 | if( db_step(&q)==SQLITE_ROW ){ |
| 101 | db_column_blob(&q, 1, pContent); |
| 102 | if( db_column_int(&q, 0)==1 ){ |
| 103 | blob_uncompress(pContent, pContent); |
| 104 | } |
| 105 | rc = 1; |
| 106 | } |
| 107 | db_finalize(&q); |
| 108 | if( rc==0 && validate16(zName,-1) ){ |
| 109 | db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE hash=%Q", |
| 110 | zName); |
| 111 | if( db_step(&q)==SQLITE_ROW ){ |
| 112 | db_column_blob(&q, 1, pContent); |
| 113 | if( db_column_int(&q, 0)==1 ){ |
| 114 | blob_uncompress(pContent, pContent); |
| 115 | } |
| 116 | rc = 2; |
| 117 | } |
| 118 | db_finalize(&q); |
| 119 | } |
| 120 | return rc; |
| 121 | } |
| 122 | |
| 123 | /* |
| 124 | ** Write unversioned content into the database. |
| @@ -328,11 +343,11 @@ | |
| 343 | int i; |
| 344 | verify_all_options(); |
| 345 | db_begin_transaction(); |
| 346 | for(i=3; i<g.argc; i++){ |
| 347 | Blob content; |
| 348 | if( unversioned_content(g.argv[i], &content)!=0 ){ |
| 349 | blob_write_to_file(&content, "-"); |
| 350 | } |
| 351 | blob_reset(&content); |
| 352 | } |
| 353 | db_end_transaction(0); |
| @@ -352,11 +367,11 @@ | |
| 367 | } |
| 368 | zTFile = fossil_temp_filename(); |
| 369 | if( zTFile==0 ) fossil_fatal("cannot find a temporary filename"); |
| 370 | db_begin_transaction(); |
| 371 | content_rcvid_init("#!fossil unversioned edit"); |
| 372 | if( unversioned_content(zUVFile, &content)==0 ){ |
| 373 | fossil_fatal("no such uv-file: %Q", zUVFile); |
| 374 | } |
| 375 | if( looks_like_binary(&content) ){ |
| 376 | fossil_fatal("cannot edit binary content"); |
| 377 | } |
| @@ -381,11 +396,11 @@ | |
| 396 | blob_reset(&content); |
| 397 | }else if( memcmp(zCmd, "export", nCmd)==0 ){ |
| 398 | Blob content; |
| 399 | verify_all_options(); |
| 400 | if( g.argc!=5 ) usage("export UVFILE OUTPUT"); |
| 401 | if( unversioned_content(g.argv[3], &content)==0 ){ |
| 402 | fossil_fatal("no such uv-file: %Q", g.argv[3]); |
| 403 | } |
| 404 | blob_write_to_file(&content, g.argv[4]); |
| 405 | blob_reset(&content); |
| 406 | }else if( memcmp(zCmd, "hash", nCmd)==0 ){ /* undocumented */ |
| 407 |
+2
-2
| --- www/cgi.wiki | ||
| +++ www/cgi.wiki | ||
| @@ -14,12 +14,12 @@ | ||
| 14 | 14 | script]. CGI is the technique that the three |
| 15 | 15 | [./selfhost.wiki|self-hosting Fossil repositories] all use. |
| 16 | 16 | |
| 17 | 17 | Setting up a Fossil server using CGI is mostly about writing a short |
| 18 | 18 | script (usually just 2 lines line) in the cgi-bin folder of an ordinary |
| 19 | -web-browser. But there are a lot of extra options that can be added | |
| 20 | -to this script, to customize the configuration. This article descripts | |
| 19 | +web-server. But there are a lot of extra options that can be added | |
| 20 | +to this script, to customize the configuration. This article describes | |
| 21 | 21 | those options. |
| 22 | 22 | |
| 23 | 23 | <h1>CGI Script Options</h1> |
| 24 | 24 | |
| 25 | 25 | The CGI script used to launch a Fossil server will usually look something |
| 26 | 26 |
| --- www/cgi.wiki | |
| +++ www/cgi.wiki | |
| @@ -14,12 +14,12 @@ | |
| 14 | script]. CGI is the technique that the three |
| 15 | [./selfhost.wiki|self-hosting Fossil repositories] all use. |
| 16 | |
| 17 | Setting up a Fossil server using CGI is mostly about writing a short |
| 18 | script (usually just 2 lines line) in the cgi-bin folder of an ordinary |
| 19 | web-browser. But there are a lot of extra options that can be added |
| 20 | to this script, to customize the configuration. This article descripts |
| 21 | those options. |
| 22 | |
| 23 | <h1>CGI Script Options</h1> |
| 24 | |
| 25 | The CGI script used to launch a Fossil server will usually look something |
| 26 |
| --- www/cgi.wiki | |
| +++ www/cgi.wiki | |
| @@ -14,12 +14,12 @@ | |
| 14 | script]. CGI is the technique that the three |
| 15 | [./selfhost.wiki|self-hosting Fossil repositories] all use. |
| 16 | |
| 17 | Setting up a Fossil server using CGI is mostly about writing a short |
| 18 | script (usually just 2 lines line) in the cgi-bin folder of an ordinary |
| 19 | web-server. But there are a lot of extra options that can be added |
| 20 | to this script, to customize the configuration. This article describes |
| 21 | those options. |
| 22 | |
| 23 | <h1>CGI Script Options</h1> |
| 24 | |
| 25 | The CGI script used to launch a Fossil server will usually look something |
| 26 |
+1
-1
| --- www/index.wiki | ||
| +++ www/index.wiki | ||
| @@ -9,11 +9,11 @@ | ||
| 9 | 9 | <li> [./build.wiki | Install] |
| 10 | 10 | <li> [https://fossil-scm.org/forum | Support/Forum ] |
| 11 | 11 | <li> [./hints.wiki | Tips & Hints] |
| 12 | 12 | <li> [./changes.wiki | Change Log] |
| 13 | 13 | <li> [../COPYRIGHT-BSD2.txt | License] |
| 14 | -<li> [./userlinks.wiki | User inks] | |
| 14 | +<li> [./userlinks.wiki | User Links] | |
| 15 | 15 | <li> [./hacker-howto.wiki | Hacker How-To] |
| 16 | 16 | <li> [./fossil-v-git.wiki | Fossil vs. Git] |
| 17 | 17 | <li> [./permutedindex.html | Documentation Index] |
| 18 | 18 | </ul> |
| 19 | 19 | <img src="fossil3.gif" align="center"> |
| 20 | 20 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -9,11 +9,11 @@ | |
| 9 | <li> [./build.wiki | Install] |
| 10 | <li> [https://fossil-scm.org/forum | Support/Forum ] |
| 11 | <li> [./hints.wiki | Tips & Hints] |
| 12 | <li> [./changes.wiki | Change Log] |
| 13 | <li> [../COPYRIGHT-BSD2.txt | License] |
| 14 | <li> [./userlinks.wiki | User inks] |
| 15 | <li> [./hacker-howto.wiki | Hacker How-To] |
| 16 | <li> [./fossil-v-git.wiki | Fossil vs. Git] |
| 17 | <li> [./permutedindex.html | Documentation Index] |
| 18 | </ul> |
| 19 | <img src="fossil3.gif" align="center"> |
| 20 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -9,11 +9,11 @@ | |
| 9 | <li> [./build.wiki | Install] |
| 10 | <li> [https://fossil-scm.org/forum | Support/Forum ] |
| 11 | <li> [./hints.wiki | Tips & Hints] |
| 12 | <li> [./changes.wiki | Change Log] |
| 13 | <li> [../COPYRIGHT-BSD2.txt | License] |
| 14 | <li> [./userlinks.wiki | User Links] |
| 15 | <li> [./hacker-howto.wiki | Hacker How-To] |
| 16 | <li> [./fossil-v-git.wiki | Fossil vs. Git] |
| 17 | <li> [./permutedindex.html | Documentation Index] |
| 18 | </ul> |
| 19 | <img src="fossil3.gif" align="center"> |
| 20 |