Fossil SCM
Created fossil_spawn() as a dumb-as-rocks wrapper for posix_spawnp(2) and created tests to prove that it doesn't double-interpret its args as fossil_system() does. This isn't portable yet; it's just a seed we can use to bring this branch to a PoC state.
Commit
fb7c5797365ee55dbeb1a70fde8fc5b8c3169be1c90fdf54f723435506abcea8
Parent
162625e212f1e56…
2 files changed
+47
+19
+47
| --- src/util.c | ||
| +++ src/util.c | ||
| @@ -34,10 +34,11 @@ | ||
| 34 | 34 | # include <sys/time.h> |
| 35 | 35 | # include <sys/resource.h> |
| 36 | 36 | # include <unistd.h> |
| 37 | 37 | # include <fcntl.h> |
| 38 | 38 | # include <errno.h> |
| 39 | +# include <spawn.h> | |
| 39 | 40 | #endif |
| 40 | 41 | |
| 41 | 42 | |
| 42 | 43 | /* |
| 43 | 44 | ** Exit. Take care to close the database first. |
| @@ -362,10 +363,56 @@ | ||
| 362 | 363 | fflush(stdout); |
| 363 | 364 | rc = fossil_system(zLine); |
| 364 | 365 | printf("result: %d\n", rc); |
| 365 | 366 | } |
| 366 | 367 | } |
| 368 | + | |
| 369 | +/* | |
| 370 | +** A cross-platform "spawn" type function: search for zProgram in PATH, | |
| 371 | +** passing the given argument list through without interpretation. Due | |
| 372 | +** to Windows platform limitations — see the definition of WinMain() — | |
| 373 | +** this is an ideal we can achieve only on POSIX platforms. | |
| 374 | +*/ | |
| 375 | +int fossil_spawn(const char* zProgram, char* const azArgv[]) | |
| 376 | +{ | |
| 377 | + extern char **environ; | |
| 378 | + int status = -1; | |
| 379 | + pid_t pid; | |
| 380 | + if(posix_spawnp(&pid, zProgram, NULL, NULL, azArgv, environ)==0){ | |
| 381 | + waitpid(pid, &status, 0); | |
| 382 | + }else{ | |
| 383 | + fossil_fatal("posix_spawn(%s, ...) failed: %s", zProgram, sys_errlist[errno]); | |
| 384 | + } | |
| 385 | + return status; | |
| 386 | +} | |
| 387 | + | |
| 388 | +/* | |
| 389 | +** COMMAND: test-fossil-spawn | |
| 390 | +** | |
| 391 | +** Calls test-echo, passing each argument as-given. Used by the test | |
| 392 | +** suite to verify that this platform can pass problematic arguments | |
| 393 | +** (quotes, spaces, shell-specific escaping characters...) through | |
| 394 | +** fossil_spawn() without mangling or reinterpretation. | |
| 395 | +** | |
| 396 | +** To an outsider, this function has the same effect as test-echo, | |
| 397 | +** but the way it achieves that exercises a different subset of | |
| 398 | +** Fossil's functionality. | |
| 399 | +*/ | |
| 400 | +void test_fossil_spawn_cmd(void){ | |
| 401 | + int i, j=0; | |
| 402 | + char** azArgv = fossil_malloc(sizeof(char*) * (g.argc + 1)); | |
| 403 | + azArgv[j++] = g.nameOfExe; | |
| 404 | + azArgv[j++] = "test-echo"; | |
| 405 | + if( find_option("hex",0,0) ){ | |
| 406 | + azArgv[j++] = "--hex"; | |
| 407 | + } | |
| 408 | + for( i=2; i<g.argc; ++i ){ | |
| 409 | + azArgv[j++] = g.argv[i]; | |
| 410 | + } | |
| 411 | + azArgv[j] = 0; | |
| 412 | + fossil_spawn(g.nameOfExe, azArgv); | |
| 413 | +} | |
| 367 | 414 | |
| 368 | 415 | /* |
| 369 | 416 | ** Like strcmp() except that it accepts NULL pointers. NULL sorts before |
| 370 | 417 | ** all non-NULL string pointers. Also, this strcmp() is a binary comparison |
| 371 | 418 | ** that does not consider locale. |
| 372 | 419 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -34,10 +34,11 @@ | |
| 34 | # include <sys/time.h> |
| 35 | # include <sys/resource.h> |
| 36 | # include <unistd.h> |
| 37 | # include <fcntl.h> |
| 38 | # include <errno.h> |
| 39 | #endif |
| 40 | |
| 41 | |
| 42 | /* |
| 43 | ** Exit. Take care to close the database first. |
| @@ -362,10 +363,56 @@ | |
| 362 | fflush(stdout); |
| 363 | rc = fossil_system(zLine); |
| 364 | printf("result: %d\n", rc); |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | /* |
| 369 | ** Like strcmp() except that it accepts NULL pointers. NULL sorts before |
| 370 | ** all non-NULL string pointers. Also, this strcmp() is a binary comparison |
| 371 | ** that does not consider locale. |
| 372 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -34,10 +34,11 @@ | |
| 34 | # include <sys/time.h> |
| 35 | # include <sys/resource.h> |
| 36 | # include <unistd.h> |
| 37 | # include <fcntl.h> |
| 38 | # include <errno.h> |
| 39 | # include <spawn.h> |
| 40 | #endif |
| 41 | |
| 42 | |
| 43 | /* |
| 44 | ** Exit. Take care to close the database first. |
| @@ -362,10 +363,56 @@ | |
| 363 | fflush(stdout); |
| 364 | rc = fossil_system(zLine); |
| 365 | printf("result: %d\n", rc); |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | /* |
| 370 | ** A cross-platform "spawn" type function: search for zProgram in PATH, |
| 371 | ** passing the given argument list through without interpretation. Due |
| 372 | ** to Windows platform limitations — see the definition of WinMain() — |
| 373 | ** this is an ideal we can achieve only on POSIX platforms. |
| 374 | */ |
| 375 | int fossil_spawn(const char* zProgram, char* const azArgv[]) |
| 376 | { |
| 377 | extern char **environ; |
| 378 | int status = -1; |
| 379 | pid_t pid; |
| 380 | if(posix_spawnp(&pid, zProgram, NULL, NULL, azArgv, environ)==0){ |
| 381 | waitpid(pid, &status, 0); |
| 382 | }else{ |
| 383 | fossil_fatal("posix_spawn(%s, ...) failed: %s", zProgram, sys_errlist[errno]); |
| 384 | } |
| 385 | return status; |
| 386 | } |
| 387 | |
| 388 | /* |
| 389 | ** COMMAND: test-fossil-spawn |
| 390 | ** |
| 391 | ** Calls test-echo, passing each argument as-given. Used by the test |
| 392 | ** suite to verify that this platform can pass problematic arguments |
| 393 | ** (quotes, spaces, shell-specific escaping characters...) through |
| 394 | ** fossil_spawn() without mangling or reinterpretation. |
| 395 | ** |
| 396 | ** To an outsider, this function has the same effect as test-echo, |
| 397 | ** but the way it achieves that exercises a different subset of |
| 398 | ** Fossil's functionality. |
| 399 | */ |
| 400 | void test_fossil_spawn_cmd(void){ |
| 401 | int i, j=0; |
| 402 | char** azArgv = fossil_malloc(sizeof(char*) * (g.argc + 1)); |
| 403 | azArgv[j++] = g.nameOfExe; |
| 404 | azArgv[j++] = "test-echo"; |
| 405 | if( find_option("hex",0,0) ){ |
| 406 | azArgv[j++] = "--hex"; |
| 407 | } |
| 408 | for( i=2; i<g.argc; ++i ){ |
| 409 | azArgv[j++] = g.argv[i]; |
| 410 | } |
| 411 | azArgv[j] = 0; |
| 412 | fossil_spawn(g.nameOfExe, azArgv); |
| 413 | } |
| 414 | |
| 415 | /* |
| 416 | ** Like strcmp() except that it accepts NULL pointers. NULL sorts before |
| 417 | ** all non-NULL string pointers. Also, this strcmp() is a binary comparison |
| 418 | ** that does not consider locale. |
| 419 |
+19
| --- test/cmdline.test | ||
| +++ test/cmdline.test | ||
| @@ -46,9 +46,28 @@ | ||
| 46 | 46 | # |
| 47 | 47 | # NOTE: Use an --args file on Windows to avoid unwanted glob expansion |
| 48 | 48 | # from MinGW and/or the MSVCRT. |
| 49 | 49 | # |
| 50 | 50 | cmd-line $is_windows 101 * * *.* *.* |
| 51 | + | |
| 52 | + | |
| 53 | +############################################################################### | |
| 54 | +# Now do similar things with the new "spawn" mechanism, checking whether | |
| 55 | +# args passed through a potential double interpretation call sequence do | |
| 56 | +# not in fact get double-interpreted. | |
| 57 | + | |
| 58 | +proc spawn {args} { | |
| 59 | + set i 1 | |
| 60 | + foreach arg $args { | |
| 61 | + fossil test-fossil-spawn $arg | |
| 62 | + test spawn-echo.$i \ | |
| 63 | + {[lrange [split $::RESULT \n] 3 end]=="\{argv\[2\] = \[$arg\]\}"} | |
| 64 | + incr i | |
| 65 | + } | |
| 66 | +} | |
| 67 | + | |
| 68 | +spawn "a" "bx" {$SHELL} {$(ls)} "%COMSPEC%" "Jörg's “Development” Job Offer" | |
| 69 | + | |
| 51 | 70 | |
| 52 | 71 | ############################################################################### |
| 53 | 72 | |
| 54 | 73 | test_cleanup |
| 55 | 74 |
| --- test/cmdline.test | |
| +++ test/cmdline.test | |
| @@ -46,9 +46,28 @@ | |
| 46 | # |
| 47 | # NOTE: Use an --args file on Windows to avoid unwanted glob expansion |
| 48 | # from MinGW and/or the MSVCRT. |
| 49 | # |
| 50 | cmd-line $is_windows 101 * * *.* *.* |
| 51 | |
| 52 | ############################################################################### |
| 53 | |
| 54 | test_cleanup |
| 55 |
| --- test/cmdline.test | |
| +++ test/cmdline.test | |
| @@ -46,9 +46,28 @@ | |
| 46 | # |
| 47 | # NOTE: Use an --args file on Windows to avoid unwanted glob expansion |
| 48 | # from MinGW and/or the MSVCRT. |
| 49 | # |
| 50 | cmd-line $is_windows 101 * * *.* *.* |
| 51 | |
| 52 | |
| 53 | ############################################################################### |
| 54 | # Now do similar things with the new "spawn" mechanism, checking whether |
| 55 | # args passed through a potential double interpretation call sequence do |
| 56 | # not in fact get double-interpreted. |
| 57 | |
| 58 | proc spawn {args} { |
| 59 | set i 1 |
| 60 | foreach arg $args { |
| 61 | fossil test-fossil-spawn $arg |
| 62 | test spawn-echo.$i \ |
| 63 | {[lrange [split $::RESULT \n] 3 end]=="\{argv\[2\] = \[$arg\]\}"} |
| 64 | incr i |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | spawn "a" "bx" {$SHELL} {$(ls)} "%COMSPEC%" "Jörg's “Development” Job Offer" |
| 69 | |
| 70 | |
| 71 | ############################################################################### |
| 72 | |
| 73 | test_cleanup |
| 74 |