Fossil SCM
Update /dir to support readme and readme.* files being a symlink to the actual document. Symlinks to symlinks are not supported in this instance. The document type is determined by the link target name.
Commit
eb4dda482056b1db9667927d0ffee24ee3a08bd834b6b39b2dcf5ca334ffddc1
Parent
50844e5c6be4836…
2 files changed
+53
-26
+3
+53
-26
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -212,21 +212,21 @@ | ||
| 212 | 212 | ** Subdirectory names begin with "/". This causes them to sort |
| 213 | 213 | ** first and it also gives us an easy way to distinguish files |
| 214 | 214 | ** from directories in the loop that follows. |
| 215 | 215 | */ |
| 216 | 216 | db_multi_exec( |
| 217 | - "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);" | |
| 217 | + "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u, l);" | |
| 218 | 218 | ); |
| 219 | 219 | if( zCI ){ |
| 220 | 220 | Stmt ins; |
| 221 | 221 | ManifestFile *pFile; |
| 222 | 222 | ManifestFile *pPrev = 0; |
| 223 | 223 | int nPrev = 0; |
| 224 | 224 | int c; |
| 225 | 225 | |
| 226 | 226 | db_prepare(&ins, |
| 227 | - "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)" | |
| 227 | + "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u, :l)" | |
| 228 | 228 | ); |
| 229 | 229 | manifest_file_rewind(pM); |
| 230 | 230 | while( (pFile = manifest_file_next(pM,0))!=0 ){ |
| 231 | 231 | if( nD>0 |
| 232 | 232 | && (fossil_strncmp(pFile->zName, zD, nD-1)!=0 |
| @@ -240,10 +240,11 @@ | ||
| 240 | 240 | ){ |
| 241 | 241 | continue; |
| 242 | 242 | } |
| 243 | 243 | db_bind_text(&ins, ":x", &pFile->zName[nD]); |
| 244 | 244 | db_bind_text(&ins, ":u", pFile->zUuid); |
| 245 | + db_bind_int(&ins, ":l", pFile->zPerm && strchr(pFile->zPerm, 'l')!=0); | |
| 245 | 246 | db_step(&ins); |
| 246 | 247 | db_reset(&ins); |
| 247 | 248 | pPrev = pFile; |
| 248 | 249 | for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){} |
| 249 | 250 | if( c=='/' ) nPrev++; |
| @@ -250,18 +251,18 @@ | ||
| 250 | 251 | } |
| 251 | 252 | db_finalize(&ins); |
| 252 | 253 | }else if( zD ){ |
| 253 | 254 | db_multi_exec( |
| 254 | 255 | "INSERT OR IGNORE INTO localfiles" |
| 255 | - " SELECT pathelement(name,%d), NULL FROM filename" | |
| 256 | + " SELECT pathelement(name,%d,0), NULL FROM filename" | |
| 256 | 257 | " WHERE name GLOB '%q/*'", |
| 257 | 258 | nD, zD |
| 258 | 259 | ); |
| 259 | 260 | }else{ |
| 260 | 261 | db_multi_exec( |
| 261 | 262 | "INSERT OR IGNORE INTO localfiles" |
| 262 | - " SELECT pathelement(name,0), NULL FROM filename" | |
| 263 | + " SELECT pathelement(name,0,0), NULL FROM filename" | |
| 263 | 264 | ); |
| 264 | 265 | } |
| 265 | 266 | |
| 266 | 267 | /* Generate a multi-column table listing the contents of zD[] |
| 267 | 268 | ** directory. |
| @@ -304,41 +305,67 @@ | ||
| 304 | 305 | |
| 305 | 306 | /* If the directory contains a readme file, then display its content below |
| 306 | 307 | ** the list of files |
| 307 | 308 | */ |
| 308 | 309 | db_prepare(&q, |
| 309 | - "SELECT x, u FROM localfiles" | |
| 310 | + "SELECT x, u, l FROM localfiles" | |
| 310 | 311 | " WHERE x COLLATE nocase IN" |
| 311 | 312 | " ('readme','readme.txt','readme.md','readme.wiki','readme.markdown'," |
| 312 | 313 | " 'readme.html') ORDER BY x LIMIT 1;" |
| 313 | 314 | ); |
| 314 | 315 | if( db_step(&q)==SQLITE_ROW ){ |
| 315 | 316 | const char *zName = db_column_text(&q,0); |
| 316 | 317 | const char *zUuid = db_column_text(&q,1); |
| 318 | + int isLink = db_column_int(&q,2); | |
| 317 | 319 | if( zUuid ){ |
| 318 | 320 | rid = fast_uuid_to_rid(zUuid); |
| 319 | - }else{ | |
| 320 | - if( zD ){ | |
| 321 | - rid = db_int(0, | |
| 322 | - "SELECT fid FROM filename, mlink, event" | |
| 323 | - " WHERE name='%q/%q'" | |
| 324 | - " AND mlink.fnid=filename.fnid" | |
| 325 | - " AND event.objid=mlink.mid" | |
| 326 | - " ORDER BY event.mtime DESC LIMIT 1", | |
| 327 | - zD, zName | |
| 328 | - ); | |
| 329 | - }else{ | |
| 330 | - rid = db_int(0, | |
| 331 | - "SELECT fid FROM filename, mlink, event" | |
| 332 | - " WHERE name='%q'" | |
| 333 | - " AND mlink.fnid=filename.fnid" | |
| 334 | - " AND event.objid=mlink.mid" | |
| 335 | - " ORDER BY event.mtime DESC LIMIT 1", | |
| 336 | - zName | |
| 337 | - ); | |
| 338 | - } | |
| 339 | - } | |
| 321 | + }else if( zD ){ | |
| 322 | + rid = db_int(0, | |
| 323 | + "SELECT fid FROM filename, mlink, event" | |
| 324 | + " WHERE name='%q/%q'" | |
| 325 | + " AND mlink.fnid=filename.fnid" | |
| 326 | + " AND event.objid=mlink.mid" | |
| 327 | + " ORDER BY event.mtime DESC LIMIT 1", | |
| 328 | + zD, zName | |
| 329 | + ); | |
| 330 | + }else{ | |
| 331 | + rid = db_int(0, | |
| 332 | + "SELECT fid FROM filename, mlink, event" | |
| 333 | + " WHERE name='%q'" | |
| 334 | + " AND mlink.fnid=filename.fnid" | |
| 335 | + " AND event.objid=mlink.mid" | |
| 336 | + " ORDER BY event.mtime DESC LIMIT 1", | |
| 337 | + zName | |
| 338 | + ); | |
| 339 | + } | |
| 340 | + | |
| 341 | + /* If the README file is a symlink, dereference it to find the actual | |
| 342 | + * document. To keep things simple and to avoid infinite loops, do not | |
| 343 | + * attempt more than one level of dereferencing. */ | |
| 344 | + if( rid && isLink ){ | |
| 345 | + char *zDir, *zNewName; | |
| 346 | + Blob content; | |
| 347 | + content_get(rid, &content); | |
| 348 | + zDir = file_dirname(zName); | |
| 349 | + if( zDir ){ | |
| 350 | + zNewName = mprintf("%s/%s", zDir, blob_buffer(&content)); | |
| 351 | + }else{ | |
| 352 | + zNewName = blob_buffer(&content); | |
| 353 | + } | |
| 354 | + file_simplify_name(zNewName, -1, 0); | |
| 355 | + rid = db_int(0, | |
| 356 | + "SELECT fid FROM filename, mlink, event" | |
| 357 | + " WHERE name='%q'" | |
| 358 | + " AND mlink.fnid=filename.fnid" | |
| 359 | + " AND event.objid=mlink.mid" | |
| 360 | + " ORDER BY event.mtime DESC LIMIT 1", | |
| 361 | + zNewName | |
| 362 | + ); | |
| 363 | + zName = zNewName; | |
| 364 | + zUuid = 0; | |
| 365 | + } | |
| 366 | + | |
| 340 | 367 | if( rid ){ |
| 341 | 368 | @ <hr> |
| 342 | 369 | if( sqlite3_strlike("readme.html", zName, 0)==0 ){ |
| 343 | 370 | if( zUuid==0 ){ |
| 344 | 371 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 345 | 372 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -212,21 +212,21 @@ | |
| 212 | ** Subdirectory names begin with "/". This causes them to sort |
| 213 | ** first and it also gives us an easy way to distinguish files |
| 214 | ** from directories in the loop that follows. |
| 215 | */ |
| 216 | db_multi_exec( |
| 217 | "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);" |
| 218 | ); |
| 219 | if( zCI ){ |
| 220 | Stmt ins; |
| 221 | ManifestFile *pFile; |
| 222 | ManifestFile *pPrev = 0; |
| 223 | int nPrev = 0; |
| 224 | int c; |
| 225 | |
| 226 | db_prepare(&ins, |
| 227 | "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)" |
| 228 | ); |
| 229 | manifest_file_rewind(pM); |
| 230 | while( (pFile = manifest_file_next(pM,0))!=0 ){ |
| 231 | if( nD>0 |
| 232 | && (fossil_strncmp(pFile->zName, zD, nD-1)!=0 |
| @@ -240,10 +240,11 @@ | |
| 240 | ){ |
| 241 | continue; |
| 242 | } |
| 243 | db_bind_text(&ins, ":x", &pFile->zName[nD]); |
| 244 | db_bind_text(&ins, ":u", pFile->zUuid); |
| 245 | db_step(&ins); |
| 246 | db_reset(&ins); |
| 247 | pPrev = pFile; |
| 248 | for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){} |
| 249 | if( c=='/' ) nPrev++; |
| @@ -250,18 +251,18 @@ | |
| 250 | } |
| 251 | db_finalize(&ins); |
| 252 | }else if( zD ){ |
| 253 | db_multi_exec( |
| 254 | "INSERT OR IGNORE INTO localfiles" |
| 255 | " SELECT pathelement(name,%d), NULL FROM filename" |
| 256 | " WHERE name GLOB '%q/*'", |
| 257 | nD, zD |
| 258 | ); |
| 259 | }else{ |
| 260 | db_multi_exec( |
| 261 | "INSERT OR IGNORE INTO localfiles" |
| 262 | " SELECT pathelement(name,0), NULL FROM filename" |
| 263 | ); |
| 264 | } |
| 265 | |
| 266 | /* Generate a multi-column table listing the contents of zD[] |
| 267 | ** directory. |
| @@ -304,41 +305,67 @@ | |
| 304 | |
| 305 | /* If the directory contains a readme file, then display its content below |
| 306 | ** the list of files |
| 307 | */ |
| 308 | db_prepare(&q, |
| 309 | "SELECT x, u FROM localfiles" |
| 310 | " WHERE x COLLATE nocase IN" |
| 311 | " ('readme','readme.txt','readme.md','readme.wiki','readme.markdown'," |
| 312 | " 'readme.html') ORDER BY x LIMIT 1;" |
| 313 | ); |
| 314 | if( db_step(&q)==SQLITE_ROW ){ |
| 315 | const char *zName = db_column_text(&q,0); |
| 316 | const char *zUuid = db_column_text(&q,1); |
| 317 | if( zUuid ){ |
| 318 | rid = fast_uuid_to_rid(zUuid); |
| 319 | }else{ |
| 320 | if( zD ){ |
| 321 | rid = db_int(0, |
| 322 | "SELECT fid FROM filename, mlink, event" |
| 323 | " WHERE name='%q/%q'" |
| 324 | " AND mlink.fnid=filename.fnid" |
| 325 | " AND event.objid=mlink.mid" |
| 326 | " ORDER BY event.mtime DESC LIMIT 1", |
| 327 | zD, zName |
| 328 | ); |
| 329 | }else{ |
| 330 | rid = db_int(0, |
| 331 | "SELECT fid FROM filename, mlink, event" |
| 332 | " WHERE name='%q'" |
| 333 | " AND mlink.fnid=filename.fnid" |
| 334 | " AND event.objid=mlink.mid" |
| 335 | " ORDER BY event.mtime DESC LIMIT 1", |
| 336 | zName |
| 337 | ); |
| 338 | } |
| 339 | } |
| 340 | if( rid ){ |
| 341 | @ <hr> |
| 342 | if( sqlite3_strlike("readme.html", zName, 0)==0 ){ |
| 343 | if( zUuid==0 ){ |
| 344 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 345 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -212,21 +212,21 @@ | |
| 212 | ** Subdirectory names begin with "/". This causes them to sort |
| 213 | ** first and it also gives us an easy way to distinguish files |
| 214 | ** from directories in the loop that follows. |
| 215 | */ |
| 216 | db_multi_exec( |
| 217 | "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u, l);" |
| 218 | ); |
| 219 | if( zCI ){ |
| 220 | Stmt ins; |
| 221 | ManifestFile *pFile; |
| 222 | ManifestFile *pPrev = 0; |
| 223 | int nPrev = 0; |
| 224 | int c; |
| 225 | |
| 226 | db_prepare(&ins, |
| 227 | "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u, :l)" |
| 228 | ); |
| 229 | manifest_file_rewind(pM); |
| 230 | while( (pFile = manifest_file_next(pM,0))!=0 ){ |
| 231 | if( nD>0 |
| 232 | && (fossil_strncmp(pFile->zName, zD, nD-1)!=0 |
| @@ -240,10 +240,11 @@ | |
| 240 | ){ |
| 241 | continue; |
| 242 | } |
| 243 | db_bind_text(&ins, ":x", &pFile->zName[nD]); |
| 244 | db_bind_text(&ins, ":u", pFile->zUuid); |
| 245 | db_bind_int(&ins, ":l", pFile->zPerm && strchr(pFile->zPerm, 'l')!=0); |
| 246 | db_step(&ins); |
| 247 | db_reset(&ins); |
| 248 | pPrev = pFile; |
| 249 | for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){} |
| 250 | if( c=='/' ) nPrev++; |
| @@ -250,18 +251,18 @@ | |
| 251 | } |
| 252 | db_finalize(&ins); |
| 253 | }else if( zD ){ |
| 254 | db_multi_exec( |
| 255 | "INSERT OR IGNORE INTO localfiles" |
| 256 | " SELECT pathelement(name,%d,0), NULL FROM filename" |
| 257 | " WHERE name GLOB '%q/*'", |
| 258 | nD, zD |
| 259 | ); |
| 260 | }else{ |
| 261 | db_multi_exec( |
| 262 | "INSERT OR IGNORE INTO localfiles" |
| 263 | " SELECT pathelement(name,0,0), NULL FROM filename" |
| 264 | ); |
| 265 | } |
| 266 | |
| 267 | /* Generate a multi-column table listing the contents of zD[] |
| 268 | ** directory. |
| @@ -304,41 +305,67 @@ | |
| 305 | |
| 306 | /* If the directory contains a readme file, then display its content below |
| 307 | ** the list of files |
| 308 | */ |
| 309 | db_prepare(&q, |
| 310 | "SELECT x, u, l FROM localfiles" |
| 311 | " WHERE x COLLATE nocase IN" |
| 312 | " ('readme','readme.txt','readme.md','readme.wiki','readme.markdown'," |
| 313 | " 'readme.html') ORDER BY x LIMIT 1;" |
| 314 | ); |
| 315 | if( db_step(&q)==SQLITE_ROW ){ |
| 316 | const char *zName = db_column_text(&q,0); |
| 317 | const char *zUuid = db_column_text(&q,1); |
| 318 | int isLink = db_column_int(&q,2); |
| 319 | if( zUuid ){ |
| 320 | rid = fast_uuid_to_rid(zUuid); |
| 321 | }else if( zD ){ |
| 322 | rid = db_int(0, |
| 323 | "SELECT fid FROM filename, mlink, event" |
| 324 | " WHERE name='%q/%q'" |
| 325 | " AND mlink.fnid=filename.fnid" |
| 326 | " AND event.objid=mlink.mid" |
| 327 | " ORDER BY event.mtime DESC LIMIT 1", |
| 328 | zD, zName |
| 329 | ); |
| 330 | }else{ |
| 331 | rid = db_int(0, |
| 332 | "SELECT fid FROM filename, mlink, event" |
| 333 | " WHERE name='%q'" |
| 334 | " AND mlink.fnid=filename.fnid" |
| 335 | " AND event.objid=mlink.mid" |
| 336 | " ORDER BY event.mtime DESC LIMIT 1", |
| 337 | zName |
| 338 | ); |
| 339 | } |
| 340 | |
| 341 | /* If the README file is a symlink, dereference it to find the actual |
| 342 | * document. To keep things simple and to avoid infinite loops, do not |
| 343 | * attempt more than one level of dereferencing. */ |
| 344 | if( rid && isLink ){ |
| 345 | char *zDir, *zNewName; |
| 346 | Blob content; |
| 347 | content_get(rid, &content); |
| 348 | zDir = file_dirname(zName); |
| 349 | if( zDir ){ |
| 350 | zNewName = mprintf("%s/%s", zDir, blob_buffer(&content)); |
| 351 | }else{ |
| 352 | zNewName = blob_buffer(&content); |
| 353 | } |
| 354 | file_simplify_name(zNewName, -1, 0); |
| 355 | rid = db_int(0, |
| 356 | "SELECT fid FROM filename, mlink, event" |
| 357 | " WHERE name='%q'" |
| 358 | " AND mlink.fnid=filename.fnid" |
| 359 | " AND event.objid=mlink.mid" |
| 360 | " ORDER BY event.mtime DESC LIMIT 1", |
| 361 | zNewName |
| 362 | ); |
| 363 | zName = zNewName; |
| 364 | zUuid = 0; |
| 365 | } |
| 366 | |
| 367 | if( rid ){ |
| 368 | @ <hr> |
| 369 | if( sqlite3_strlike("readme.html", zName, 0)==0 ){ |
| 370 | if( zUuid==0 ){ |
| 371 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 372 |
+3
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -30,10 +30,13 @@ | ||
| 30 | 30 | [/help?cmd=zip|zip], and [/help?cmd=tarball|tarball] pages and commands to |
| 31 | 31 | honor the versioned manifest setting when outside of an open checkout |
| 32 | 32 | directory. |
| 33 | 33 | * The admin-log and access-log settings are now on by default for |
| 34 | 34 | new repositories. |
| 35 | + * Update /dir to support readme and readme.* files being a symlink to the | |
| 36 | + actual document. Symlinks to symlinks are not supported in this instance. | |
| 37 | + The document type is determined by the link target name. | |
| 35 | 38 | * Update the built-in SQLite to version 3.20.1. |
| 36 | 39 | |
| 37 | 40 | <a name='v2_3'></a> |
| 38 | 41 | <h2>Changes for Version 2.3 (2017-07-21)</h2> |
| 39 | 42 | |
| 40 | 43 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -30,10 +30,13 @@ | |
| 30 | [/help?cmd=zip|zip], and [/help?cmd=tarball|tarball] pages and commands to |
| 31 | honor the versioned manifest setting when outside of an open checkout |
| 32 | directory. |
| 33 | * The admin-log and access-log settings are now on by default for |
| 34 | new repositories. |
| 35 | * Update the built-in SQLite to version 3.20.1. |
| 36 | |
| 37 | <a name='v2_3'></a> |
| 38 | <h2>Changes for Version 2.3 (2017-07-21)</h2> |
| 39 | |
| 40 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -30,10 +30,13 @@ | |
| 30 | [/help?cmd=zip|zip], and [/help?cmd=tarball|tarball] pages and commands to |
| 31 | honor the versioned manifest setting when outside of an open checkout |
| 32 | directory. |
| 33 | * The admin-log and access-log settings are now on by default for |
| 34 | new repositories. |
| 35 | * Update /dir to support readme and readme.* files being a symlink to the |
| 36 | actual document. Symlinks to symlinks are not supported in this instance. |
| 37 | The document type is determined by the link target name. |
| 38 | * Update the built-in SQLite to version 3.20.1. |
| 39 | |
| 40 | <a name='v2_3'></a> |
| 41 | <h2>Changes for Version 2.3 (2017-07-21)</h2> |
| 42 | |
| 43 |