Fossil SCM
Fix the file_simplify_name() utility function so that it works with relative pathnames. Ticket [99caf06e17bed849146]
Commit
c637a8ac2d60b94eaff4bad82464f86ba4bcbf6a
Parent
dddaebf605b2ce1…
1 file changed
+64
-6
+64
-6
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -229,48 +229,106 @@ | ||
| 229 | 229 | } |
| 230 | 230 | } |
| 231 | 231 | if( z[i-1]=='/' ) return 0; |
| 232 | 232 | return 1; |
| 233 | 233 | } |
| 234 | + | |
| 235 | +/* | |
| 236 | +** If the last component of the pathname in z[0]..z[j-1] is something | |
| 237 | +** other than ".." then back it out and return true. If the last | |
| 238 | +** component is empty or if it is ".." then return false. | |
| 239 | +*/ | |
| 240 | +static int backup_dir(const char *z, int *pJ){ | |
| 241 | + int j = *pJ; | |
| 242 | + int i; | |
| 243 | + if( j<=0 ) return 0; | |
| 244 | + for(i=j-1; i>0 && z[i-1]!='/'; i--){} | |
| 245 | + if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; | |
| 246 | + *pJ = i-1; | |
| 247 | + return 1; | |
| 248 | +} | |
| 234 | 249 | |
| 235 | 250 | /* |
| 236 | 251 | ** Simplify a filename by |
| 237 | 252 | ** |
| 253 | +** * Convert all \ into / on windows | |
| 238 | 254 | ** * removing any trailing and duplicate / |
| 239 | 255 | ** * removing /./ |
| 240 | 256 | ** * removing /A/../ |
| 241 | 257 | ** |
| 242 | 258 | ** Changes are made in-place. Return the new name length. |
| 243 | 259 | */ |
| 244 | 260 | int file_simplify_name(char *z, int n){ |
| 245 | 261 | int i, j; |
| 246 | 262 | if( n<0 ) n = strlen(z); |
| 263 | + | |
| 264 | + /* On windows convert all \ characters to / */ | |
| 247 | 265 | #if defined(_WIN32) |
| 248 | 266 | for(i=0; i<n; i++){ |
| 249 | 267 | if( z[i]=='\\' ) z[i] = '/'; |
| 250 | 268 | } |
| 251 | 269 | #endif |
| 270 | + | |
| 271 | + /* Removing trailing "/" characters */ | |
| 252 | 272 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 273 | + | |
| 274 | + /* Remove duplicate '/' characters */ | |
| 253 | 275 | for(i=j=0; i<n; i++){ |
| 276 | + z[j++] = z[i]; | |
| 277 | + while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++; | |
| 278 | + } | |
| 279 | + n = j; | |
| 280 | + | |
| 281 | + /* Skip over zero or more initial "./" sequences */ | |
| 282 | + for(i=0; i<n-1 && z[i]=='.' && z[i+1]=='/'; i+=2){} | |
| 283 | + | |
| 284 | + /* Begin copying from z[i] back to z[j]... */ | |
| 285 | + for(j=0; i<n; i++){ | |
| 254 | 286 | if( z[i]=='/' ){ |
| 255 | - if( z[i+1]=='/' ) continue; | |
| 287 | + /* Skip over internal "/." directory components */ | |
| 256 | 288 | if( z[i+1]=='.' && (i+2==n || z[i+2]=='/') ){ |
| 257 | 289 | i += 1; |
| 258 | 290 | continue; |
| 259 | 291 | } |
| 260 | - if( z[i+1]=='.' && i+2<n && z[i+2]=='.' && (i+3==n || z[i+3]=='/') ){ | |
| 261 | - while( j>0 && z[j-1]!='/' ){ j--; } | |
| 262 | - if( j>0 ){ j--; } | |
| 292 | + | |
| 293 | + /* If this is a "/.." directory component then back out the | |
| 294 | + ** previous term of the directory if it is something other than ".." | |
| 295 | + ** or "." | |
| 296 | + */ | |
| 297 | + if( z[i+1]=='.' && i+2<n && z[i+2]=='.' && (i+3==n || z[i+3]=='/') | |
| 298 | + && backup_dir(z, &j) | |
| 299 | + ){ | |
| 263 | 300 | i += 2; |
| 264 | 301 | continue; |
| 265 | 302 | } |
| 266 | 303 | } |
| 267 | - z[j++] = z[i]; | |
| 304 | + if( j>=0 ) z[j] = z[i]; | |
| 305 | + j++; | |
| 268 | 306 | } |
| 307 | + if( j==0 ) z[j++] = '.'; | |
| 269 | 308 | z[j] = 0; |
| 270 | 309 | return j; |
| 271 | 310 | } |
| 311 | + | |
| 312 | +/* | |
| 313 | +** COMMAND: test-simplify-name | |
| 314 | +** | |
| 315 | +** %fossil test-simplify-name FILENAME... | |
| 316 | +** | |
| 317 | +** Print the simplified versions of each FILENAME. | |
| 318 | +*/ | |
| 319 | +void cmd_test_simplify_name(void){ | |
| 320 | + int i; | |
| 321 | + char *z; | |
| 322 | + for(i=2; i<g.argc; i++){ | |
| 323 | + z = mprintf("%s", g.argv[i]); | |
| 324 | + printf("[%s] -> ", z); | |
| 325 | + file_simplify_name(z, -1); | |
| 326 | + printf("[%s]\n", z); | |
| 327 | + fossil_free(z); | |
| 328 | + } | |
| 329 | +} | |
| 272 | 330 | |
| 273 | 331 | /* |
| 274 | 332 | ** Compute a canonical pathname for a file or directory. |
| 275 | 333 | ** Make the name absolute if it is relative. |
| 276 | 334 | ** Remove redundant / characters |
| @@ -312,11 +370,11 @@ | ||
| 312 | 370 | blob_zero(&x); |
| 313 | 371 | for(i=2; i<g.argc; i++){ |
| 314 | 372 | char zBuf[100]; |
| 315 | 373 | const char *zName = g.argv[i]; |
| 316 | 374 | file_canonical_name(zName, &x); |
| 317 | - printf("%s\n", blob_buffer(&x)); | |
| 375 | + printf("[%s] -> [%s]\n", zName, blob_buffer(&x)); | |
| 318 | 376 | blob_reset(&x); |
| 319 | 377 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zName)); |
| 320 | 378 | printf(" file_size = %s\n", zBuf); |
| 321 | 379 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_mtime(zName)); |
| 322 | 380 | printf(" file_mtime = %s\n", zBuf); |
| 323 | 381 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -229,48 +229,106 @@ | |
| 229 | } |
| 230 | } |
| 231 | if( z[i-1]=='/' ) return 0; |
| 232 | return 1; |
| 233 | } |
| 234 | |
| 235 | /* |
| 236 | ** Simplify a filename by |
| 237 | ** |
| 238 | ** * removing any trailing and duplicate / |
| 239 | ** * removing /./ |
| 240 | ** * removing /A/../ |
| 241 | ** |
| 242 | ** Changes are made in-place. Return the new name length. |
| 243 | */ |
| 244 | int file_simplify_name(char *z, int n){ |
| 245 | int i, j; |
| 246 | if( n<0 ) n = strlen(z); |
| 247 | #if defined(_WIN32) |
| 248 | for(i=0; i<n; i++){ |
| 249 | if( z[i]=='\\' ) z[i] = '/'; |
| 250 | } |
| 251 | #endif |
| 252 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 253 | for(i=j=0; i<n; i++){ |
| 254 | if( z[i]=='/' ){ |
| 255 | if( z[i+1]=='/' ) continue; |
| 256 | if( z[i+1]=='.' && (i+2==n || z[i+2]=='/') ){ |
| 257 | i += 1; |
| 258 | continue; |
| 259 | } |
| 260 | if( z[i+1]=='.' && i+2<n && z[i+2]=='.' && (i+3==n || z[i+3]=='/') ){ |
| 261 | while( j>0 && z[j-1]!='/' ){ j--; } |
| 262 | if( j>0 ){ j--; } |
| 263 | i += 2; |
| 264 | continue; |
| 265 | } |
| 266 | } |
| 267 | z[j++] = z[i]; |
| 268 | } |
| 269 | z[j] = 0; |
| 270 | return j; |
| 271 | } |
| 272 | |
| 273 | /* |
| 274 | ** Compute a canonical pathname for a file or directory. |
| 275 | ** Make the name absolute if it is relative. |
| 276 | ** Remove redundant / characters |
| @@ -312,11 +370,11 @@ | |
| 312 | blob_zero(&x); |
| 313 | for(i=2; i<g.argc; i++){ |
| 314 | char zBuf[100]; |
| 315 | const char *zName = g.argv[i]; |
| 316 | file_canonical_name(zName, &x); |
| 317 | printf("%s\n", blob_buffer(&x)); |
| 318 | blob_reset(&x); |
| 319 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zName)); |
| 320 | printf(" file_size = %s\n", zBuf); |
| 321 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_mtime(zName)); |
| 322 | printf(" file_mtime = %s\n", zBuf); |
| 323 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -229,48 +229,106 @@ | |
| 229 | } |
| 230 | } |
| 231 | if( z[i-1]=='/' ) return 0; |
| 232 | return 1; |
| 233 | } |
| 234 | |
| 235 | /* |
| 236 | ** If the last component of the pathname in z[0]..z[j-1] is something |
| 237 | ** other than ".." then back it out and return true. If the last |
| 238 | ** component is empty or if it is ".." then return false. |
| 239 | */ |
| 240 | static int backup_dir(const char *z, int *pJ){ |
| 241 | int j = *pJ; |
| 242 | int i; |
| 243 | if( j<=0 ) return 0; |
| 244 | for(i=j-1; i>0 && z[i-1]!='/'; i--){} |
| 245 | if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; |
| 246 | *pJ = i-1; |
| 247 | return 1; |
| 248 | } |
| 249 | |
| 250 | /* |
| 251 | ** Simplify a filename by |
| 252 | ** |
| 253 | ** * Convert all \ into / on windows |
| 254 | ** * removing any trailing and duplicate / |
| 255 | ** * removing /./ |
| 256 | ** * removing /A/../ |
| 257 | ** |
| 258 | ** Changes are made in-place. Return the new name length. |
| 259 | */ |
| 260 | int file_simplify_name(char *z, int n){ |
| 261 | int i, j; |
| 262 | if( n<0 ) n = strlen(z); |
| 263 | |
| 264 | /* On windows convert all \ characters to / */ |
| 265 | #if defined(_WIN32) |
| 266 | for(i=0; i<n; i++){ |
| 267 | if( z[i]=='\\' ) z[i] = '/'; |
| 268 | } |
| 269 | #endif |
| 270 | |
| 271 | /* Removing trailing "/" characters */ |
| 272 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 273 | |
| 274 | /* Remove duplicate '/' characters */ |
| 275 | for(i=j=0; i<n; i++){ |
| 276 | z[j++] = z[i]; |
| 277 | while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++; |
| 278 | } |
| 279 | n = j; |
| 280 | |
| 281 | /* Skip over zero or more initial "./" sequences */ |
| 282 | for(i=0; i<n-1 && z[i]=='.' && z[i+1]=='/'; i+=2){} |
| 283 | |
| 284 | /* Begin copying from z[i] back to z[j]... */ |
| 285 | for(j=0; i<n; i++){ |
| 286 | if( z[i]=='/' ){ |
| 287 | /* Skip over internal "/." directory components */ |
| 288 | if( z[i+1]=='.' && (i+2==n || z[i+2]=='/') ){ |
| 289 | i += 1; |
| 290 | continue; |
| 291 | } |
| 292 | |
| 293 | /* If this is a "/.." directory component then back out the |
| 294 | ** previous term of the directory if it is something other than ".." |
| 295 | ** or "." |
| 296 | */ |
| 297 | if( z[i+1]=='.' && i+2<n && z[i+2]=='.' && (i+3==n || z[i+3]=='/') |
| 298 | && backup_dir(z, &j) |
| 299 | ){ |
| 300 | i += 2; |
| 301 | continue; |
| 302 | } |
| 303 | } |
| 304 | if( j>=0 ) z[j] = z[i]; |
| 305 | j++; |
| 306 | } |
| 307 | if( j==0 ) z[j++] = '.'; |
| 308 | z[j] = 0; |
| 309 | return j; |
| 310 | } |
| 311 | |
| 312 | /* |
| 313 | ** COMMAND: test-simplify-name |
| 314 | ** |
| 315 | ** %fossil test-simplify-name FILENAME... |
| 316 | ** |
| 317 | ** Print the simplified versions of each FILENAME. |
| 318 | */ |
| 319 | void cmd_test_simplify_name(void){ |
| 320 | int i; |
| 321 | char *z; |
| 322 | for(i=2; i<g.argc; i++){ |
| 323 | z = mprintf("%s", g.argv[i]); |
| 324 | printf("[%s] -> ", z); |
| 325 | file_simplify_name(z, -1); |
| 326 | printf("[%s]\n", z); |
| 327 | fossil_free(z); |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | /* |
| 332 | ** Compute a canonical pathname for a file or directory. |
| 333 | ** Make the name absolute if it is relative. |
| 334 | ** Remove redundant / characters |
| @@ -312,11 +370,11 @@ | |
| 370 | blob_zero(&x); |
| 371 | for(i=2; i<g.argc; i++){ |
| 372 | char zBuf[100]; |
| 373 | const char *zName = g.argv[i]; |
| 374 | file_canonical_name(zName, &x); |
| 375 | printf("[%s] -> [%s]\n", zName, blob_buffer(&x)); |
| 376 | blob_reset(&x); |
| 377 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zName)); |
| 378 | printf(" file_size = %s\n", zBuf); |
| 379 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_mtime(zName)); |
| 380 | printf(" file_mtime = %s\n", zBuf); |
| 381 |