@@ -337,14 +337,12 @@ extern char **environ;
337
337
#include <windows.h>
338
338
#include <process.h>
339
339
static char * pack_env_hash(Parrot_Interp interp, PMC* hash_pmc) {
340
- Hash *hash = VTABLE_get_pointer(interp, hash_pmc);
340
+ Hash *hash = (Hash *) VTABLE_get_pointer(interp, hash_pmc);
341
341
STRING *equal = Parrot_str_new_constant(interp, "=");
342
342
STRING *key, *value, *env_var, *env_var_with_null;
343
- INTVAL hash_size = Parrot_hash_size(interp, hash);
344
343
STRING *packed = Parrot_str_new_constant(interp, "");
345
344
STRING *null = Parrot_str_new(interp, "\0", 1);
346
345
347
-
348
346
/* Parrot_hash_value_to_string is not exported*/
349
347
parrot_hash_iterate(hash,
350
348
key = (STRING *)_bucket->key;
@@ -355,7 +353,208 @@ static char * pack_env_hash(Parrot_Interp interp, PMC* hash_pmc) {
355
353
);
356
354
return Parrot_str_to_cstring(interp, packed);
357
355
}
356
+ #endif
357
+ static char **pack_arg_array(Parrot_Interp interp, PMC* array_pmc) {
358
+ INTVAL array_size = VTABLE_elements(interp, array_pmc);
359
+ char **packed = (char **)mem_sys_allocate((array_size + 1) * sizeof(char *));
360
+ INTVAL i = 0;
361
+
362
+ if (array_size > 0) {
363
+ for (i = 0; i < array_size; i++) {
364
+ PMC *pmc = VTABLE_get_pmc_keyed_int(interp, array_pmc, i);
365
+ STRING *str = (STRING *)VTABLE_get_string(interp, pmc);
366
+ #ifdef Win32
367
+ (char *)packed[i] = Parrot_str_to_cstring(interp, str);
368
+ #else
369
+ packed[i] = Parrot_str_to_cstring(interp, str);
370
+ #endif
371
+ }
372
+ }
373
+ packed[i] = NULL;
374
+
375
+ return packed;
376
+ }
377
+
378
+ static int is_space(unsigned char c) {
379
+ return c == 0x09 || c == 0x0A || c == 0x0B
380
+ || c == 0x0C || c == 0x0D || c == 0x20
381
+ || c == 0x85;
382
+ }
383
+
384
+ static char *find_next_space(const char *s) {
385
+ short in_quotes = 0;
386
+ while (*s) {
387
+ /* ignore doubled backslashes, or backslash+quote */
388
+ if (*s == '\\' && (s[1] == '\\' || s[1] == '"')) {
389
+ s += 2;
390
+ }
391
+ /* keep track of when we're within quotes */
392
+ else if (*s == '"') {
393
+ s++;
394
+ in_quotes = !in_quotes;
395
+ }
396
+ /* break it up only at spaces that aren't in quotes */
397
+ else if (!in_quotes && is_space(*s))
398
+ return (char*)s;
399
+ else
400
+ s++;
401
+ }
402
+ return (char*)s;
403
+ }
404
+
405
+ /* Autoquoting command-line arguments for nqp::spawn. */
406
+ static char *create_command_line(const char *const *args) {
407
+ int index, argc;
408
+ char *cmd, *ptr;
409
+ const char *arg;
410
+ size_t len = 0;
411
+ short bat_file = 0;
412
+ short cmd_shell = 0;
413
+ short dumb_shell = 0;
414
+ short extra_quotes = 0;
415
+ short quote_next = 0;
416
+ char *cname = (char*)args[0];
417
+ size_t clen = strlen(cname);
418
+
419
+ /* The NT cmd.exe shell has the following peculiarity that needs to be
420
+ * worked around. It strips a leading and trailing dquote when any
421
+ * of the following is true:
422
+ * 1. the /S switch was used
423
+ * 2. there are more than two dquotes
424
+ * 3. there is a special character from this set: &<>()@^|
425
+ * 4. no whitespace characters within the two dquotes
426
+ * 5. string between two dquotes isn't an executable file
427
+ * To work around this, we always add a leading and trailing dquote
428
+ * to the string, if the first argument is either "cmd.exe" or "cmd",
429
+ * and there were at least two or more arguments passed to cmd.exe
430
+ * (not including switches).
431
+ * XXX the above rules (from "cmd /?") don't seem to be applied
432
+ * always, making for the convolutions below :-(
433
+ */
434
+
435
+ #ifdef WIN32
436
+ if (cname) {
437
+ if (!clen)
438
+ clen = strlen(cname);
439
+
440
+ if (clen > 4
441
+ && (stricmp(&cname[clen-4], ".bat") == 0
442
+ || (stricmp(&cname[clen-4], ".cmd") == 0))) {
443
+ bat_file = 1;
444
+ len += 3;
445
+ }
446
+ else {
447
+ char *exe = strrchr(cname, '/');
448
+ char *exe2 = strrchr(cname, '\\');
449
+ if (exe2 > exe)
450
+ exe = exe2;
451
+ if (exe)
452
+ ++exe;
453
+ else
454
+ exe = cname;
455
+
456
+ if (stricmp(exe, "cmd.exe") == 0 || stricmp(exe, "cmd") == 0) {
457
+ cmd_shell = 1;
458
+ len += 3;
459
+ }
460
+ else if (stricmp(exe, "command.com") == 0
461
+ || stricmp(exe, "command") == 0) {
462
+ dumb_shell = 1;
463
+ }
464
+ }
465
+ }
466
+ #endif
467
+
468
+ for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
469
+ size_t curlen = strlen(arg);
470
+ if (!(arg[0] == '"' && arg[curlen-1] == '"'))
471
+ len += 2; /* assume quoting needed (worst case) */
472
+ len += curlen + 1;
473
+ }
474
+
475
+ argc = index;
476
+ cmd = (char *)mem_sys_allocate(len * sizeof(char));
477
+ ptr = cmd;
478
+
479
+ if (bat_file) {
480
+ *ptr++ = '"';
481
+ extra_quotes = 1;
482
+ }
483
+
484
+ for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
485
+ short do_quote = 0;
486
+ size_t curlen = strlen(arg);
358
487
488
+ /* we want to protect empty arguments and ones with spaces with
489
+ * dquotes, but only if they aren't already there */
490
+ if (!dumb_shell) {
491
+ if (!curlen) {
492
+ do_quote = 1;
493
+ }
494
+ else if (quote_next) {
495
+ /* see if it really is multiple arguments pretending to
496
+ * be one and force a set of quotes around it */
497
+ if (*find_next_space(arg))
498
+ do_quote = 1;
499
+ }
500
+ else if (!(arg[0] == '"' && curlen > 1 && arg[curlen-1] == '"')) {
501
+ size_t i = 0;
502
+ while (i < curlen) {
503
+ /* is space */
504
+ if (is_space(arg[i])) {
505
+ do_quote = 1;
506
+ }
507
+ else if (arg[i] == '"') {
508
+ do_quote = 0;
509
+ break;
510
+ }
511
+ i++;
512
+ }
513
+ }
514
+ }
515
+
516
+ if (do_quote)
517
+ *ptr++ = '"';
518
+
519
+ strcpy(ptr, arg);
520
+ ptr += curlen;
521
+
522
+ if (do_quote)
523
+ *ptr++ = '"';
524
+
525
+ if (args[index+1])
526
+ *ptr++ = ' ';
527
+
528
+ #ifdef WIN32
529
+ if (!extra_quotes
530
+ && cmd_shell
531
+ && curlen >= 2
532
+ && *arg == '/' /* see if arg is "/c", "/x/c", "/x/d/c" etc. */
533
+ && stricmp(arg+curlen-2, "/c") == 0) {
534
+ /* is there a next argument? */
535
+ if (args[index+1]) {
536
+ /* are there two or more next arguments? */
537
+ if (args[index+2]) {
538
+ *ptr++ = '"';
539
+ extra_quotes = 1;
540
+ }
541
+ else {
542
+ /* single argument, force quoting if it has spaces */
543
+ quote_next = 1;
544
+ }
545
+ }
546
+ }
547
+ #endif
548
+ }
549
+
550
+ if (extra_quotes)
551
+ *ptr++ = '"';
552
+
553
+ *ptr = '\0';
554
+
555
+ return cmd;
556
+ }
557
+ #ifdef WIN32
359
558
static INTVAL Run_OS_Command(PARROT_INTERP, STRING *command, PMC *env_hash)
360
559
{
361
560
DWORD status = 0;
@@ -401,11 +600,11 @@ static INTVAL Run_OS_Command(PARROT_INTERP, STRING *command, PMC *env_hash)
401
600
#include <sys/wait.h>
402
601
403
602
static char ** pack_env_hash(Parrot_Interp interp, PMC* hash_pmc) {
404
- Hash *hash = VTABLE_get_pointer(interp, hash_pmc);
603
+ Hash *hash = (Hash *) VTABLE_get_pointer(interp, hash_pmc);
405
604
STRING *equal = Parrot_str_new_constant(interp, "=");
406
605
STRING *key, *value, *env_var;
407
606
INTVAL hash_size = Parrot_hash_size(interp, hash);
408
- char** packed = mem_sys_allocate_zeroed(sizeof(char*) * (hash_size+1));
607
+ char** packed = (char **) mem_sys_allocate_zeroed(sizeof(char*) * (hash_size+1));
409
608
INTVAL i = 0;
410
609
411
610
@@ -3539,10 +3738,18 @@ inline op nqp_delete_f(out INT, in STR) :base_core {
3539
3738
#endif
3540
3739
}
3541
3740
3542
- inline op nqp_gethostname(out STR) {
3543
- char hostname[65];
3544
- gethostname(hostname, 65);
3545
- $1 = Parrot_str_new_init(interp, hostname, strlen(hostname), Parrot_utf8_encoding_ptr, 0);
3741
+ inline op nqp_spawn(out INT, in PMC, in STR, in PMC) {
3742
+ STRING *dir = $3;
3743
+ PMC *env = $4;
3744
+ char *const *argv = pack_arg_array(interp, $2);
3745
+ char *args = create_command_line(argv);
3746
+ STRING *command = Parrot_str_new(interp, args, 0);
3747
+ STRING * const old_cwd = Parrot_file_getcwd(interp);
3748
+ Parrot_str_free_cstring(args);
3749
+
3750
+ Parrot_file_chdir(interp, dir);
3751
+ $1 = Run_OS_Command(interp, command, env);
3752
+ Parrot_file_chdir(interp, old_cwd);
3546
3753
}
3547
3754
3548
3755
inline op nqp_shell(out INT, in STR, in STR, in PMC) {
@@ -3576,6 +3783,12 @@ inline op nqp_getenvhash(out PMC) {
3576
3783
}
3577
3784
}
3578
3785
3786
+ inline op nqp_gethostname(out STR) {
3787
+ char hostname[65];
3788
+ gethostname(hostname, 65);
3789
+ $1 = Parrot_str_new_init(interp, hostname, strlen(hostname), Parrot_utf8_encoding_ptr, 0);
3790
+ }
3791
+
3579
3792
/*
3580
3793
3581
3794
=item nqp_encode
0 commit comments