Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
implemented our own nqp::spawn()
  • Loading branch information
FROGGS committed Oct 29, 2013
1 parent 1161844 commit 54f6198
Show file tree
Hide file tree
Showing 2 changed files with 223 additions and 9 deletions.
1 change: 1 addition & 0 deletions src/vm/parrot/QAST/Operations.nqp
Expand Up @@ -2514,6 +2514,7 @@ QAST::Operations.add_core_pirop_mapping('nfarunalt', 'nqp_nfa_run_alt', '0PsiPPP
QAST::Operations.add_core_pirop_mapping('exit', 'exit', '0i', :inlinable(1));
QAST::Operations.add_core_pirop_mapping('sleep', 'sleep', '0n', :inlinable(1));
QAST::Operations.add_core_pirop_mapping('gethostname', 'nqp_gethostname', 'S');
QAST::Operations.add_core_pirop_mapping('spawn', 'nqp_spawn', 'IPsP');
QAST::Operations.add_core_pirop_mapping('shell', 'nqp_shell', 'IssP');
QAST::Operations.add_core_pirop_mapping('getenvhash', 'nqp_getenvhash', 'P');

Expand Down
231 changes: 222 additions & 9 deletions src/vm/parrot/ops/nqp.ops
Expand Up @@ -337,14 +337,12 @@ extern char **environ;
#include <windows.h>
#include <process.h>
static char * pack_env_hash(Parrot_Interp interp, PMC* hash_pmc) {
Hash *hash = VTABLE_get_pointer(interp, hash_pmc);
Hash *hash = (Hash *)VTABLE_get_pointer(interp, hash_pmc);
STRING *equal = Parrot_str_new_constant(interp, "=");
STRING *key, *value, *env_var, *env_var_with_null;
INTVAL hash_size = Parrot_hash_size(interp, hash);
STRING *packed = Parrot_str_new_constant(interp, "");
STRING *null = Parrot_str_new(interp, "\0", 1);


/* Parrot_hash_value_to_string is not exported*/
parrot_hash_iterate(hash,
key = (STRING *)_bucket->key;
Expand All @@ -355,7 +353,208 @@ static char * pack_env_hash(Parrot_Interp interp, PMC* hash_pmc) {
);
return Parrot_str_to_cstring(interp, packed);
}
#endif
static char **pack_arg_array(Parrot_Interp interp, PMC* array_pmc) {
INTVAL array_size = VTABLE_elements(interp, array_pmc);
char **packed = (char **)mem_sys_allocate((array_size + 1) * sizeof(char *));
INTVAL i = 0;

if (array_size > 0) {
for (i = 0; i < array_size; i++) {
PMC *pmc = VTABLE_get_pmc_keyed_int(interp, array_pmc, i);
STRING *str = (STRING *)VTABLE_get_string(interp, pmc);
#ifdef Win32
(char *)packed[i] = Parrot_str_to_cstring(interp, str);
#else
packed[i] = Parrot_str_to_cstring(interp, str);
#endif
}
}
packed[i] = NULL;

return packed;
}

static int is_space(unsigned char c) {
return c == 0x09 || c == 0x0A || c == 0x0B
|| c == 0x0C || c == 0x0D || c == 0x20
|| c == 0x85;
}

static char *find_next_space(const char *s) {
short in_quotes = 0;
while (*s) {
/* ignore doubled backslashes, or backslash+quote */
if (*s == '\\' && (s[1] == '\\' || s[1] == '"')) {
s += 2;
}
/* keep track of when we're within quotes */
else if (*s == '"') {
s++;
in_quotes = !in_quotes;
}
/* break it up only at spaces that aren't in quotes */
else if (!in_quotes && is_space(*s))
return (char*)s;
else
s++;
}
return (char*)s;
}

/* Autoquoting command-line arguments for nqp::spawn. */
static char *create_command_line(const char *const *args) {
int index, argc;
char *cmd, *ptr;
const char *arg;
size_t len = 0;
short bat_file = 0;
short cmd_shell = 0;
short dumb_shell = 0;
short extra_quotes = 0;
short quote_next = 0;
char *cname = (char*)args[0];
size_t clen = strlen(cname);

/* The NT cmd.exe shell has the following peculiarity that needs to be
* worked around. It strips a leading and trailing dquote when any
* of the following is true:
* 1. the /S switch was used
* 2. there are more than two dquotes
* 3. there is a special character from this set: &<>()@^|
* 4. no whitespace characters within the two dquotes
* 5. string between two dquotes isn't an executable file
* To work around this, we always add a leading and trailing dquote
* to the string, if the first argument is either "cmd.exe" or "cmd",
* and there were at least two or more arguments passed to cmd.exe
* (not including switches).
* XXX the above rules (from "cmd /?") don't seem to be applied
* always, making for the convolutions below :-(
*/

#ifdef WIN32
if (cname) {
if (!clen)
clen = strlen(cname);

if (clen > 4
&& (stricmp(&cname[clen-4], ".bat") == 0
|| (stricmp(&cname[clen-4], ".cmd") == 0))) {
bat_file = 1;
len += 3;
}
else {
char *exe = strrchr(cname, '/');
char *exe2 = strrchr(cname, '\\');
if (exe2 > exe)
exe = exe2;
if (exe)
++exe;
else
exe = cname;

if (stricmp(exe, "cmd.exe") == 0 || stricmp(exe, "cmd") == 0) {
cmd_shell = 1;
len += 3;
}
else if (stricmp(exe, "command.com") == 0
|| stricmp(exe, "command") == 0) {
dumb_shell = 1;
}
}
}
#endif

for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
size_t curlen = strlen(arg);
if (!(arg[0] == '"' && arg[curlen-1] == '"'))
len += 2; /* assume quoting needed (worst case) */
len += curlen + 1;
}

argc = index;
cmd = (char *)mem_sys_allocate(len * sizeof(char));
ptr = cmd;

if (bat_file) {
*ptr++ = '"';
extra_quotes = 1;
}

for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
short do_quote = 0;
size_t curlen = strlen(arg);

/* we want to protect empty arguments and ones with spaces with
* dquotes, but only if they aren't already there */
if (!dumb_shell) {
if (!curlen) {
do_quote = 1;
}
else if (quote_next) {
/* see if it really is multiple arguments pretending to
* be one and force a set of quotes around it */
if (*find_next_space(arg))
do_quote = 1;
}
else if (!(arg[0] == '"' && curlen > 1 && arg[curlen-1] == '"')) {
size_t i = 0;
while (i < curlen) {
/* is space */
if (is_space(arg[i])) {
do_quote = 1;
}
else if (arg[i] == '"') {
do_quote = 0;
break;
}
i++;
}
}
}

if (do_quote)
*ptr++ = '"';

strcpy(ptr, arg);
ptr += curlen;

if (do_quote)
*ptr++ = '"';

if (args[index+1])
*ptr++ = ' ';

#ifdef WIN32
if (!extra_quotes
&& cmd_shell
&& curlen >= 2
&& *arg == '/' /* see if arg is "/c", "/x/c", "/x/d/c" etc. */
&& stricmp(arg+curlen-2, "/c") == 0) {
/* is there a next argument? */
if (args[index+1]) {
/* are there two or more next arguments? */
if (args[index+2]) {
*ptr++ = '"';
extra_quotes = 1;
}
else {
/* single argument, force quoting if it has spaces */
quote_next = 1;
}
}
}
#endif
}

if (extra_quotes)
*ptr++ = '"';

*ptr = '\0';

return cmd;
}
#ifdef WIN32
static INTVAL Run_OS_Command(PARROT_INTERP, STRING *command, PMC *env_hash)
{
DWORD status = 0;
Expand Down Expand Up @@ -401,11 +600,11 @@ static INTVAL Run_OS_Command(PARROT_INTERP, STRING *command, PMC *env_hash)
#include <sys/wait.h>

static char ** pack_env_hash(Parrot_Interp interp, PMC* hash_pmc) {
Hash *hash = VTABLE_get_pointer(interp, hash_pmc);
Hash *hash = (Hash *)VTABLE_get_pointer(interp, hash_pmc);
STRING *equal = Parrot_str_new_constant(interp, "=");
STRING *key, *value, *env_var;
INTVAL hash_size = Parrot_hash_size(interp, hash);
char** packed = mem_sys_allocate_zeroed(sizeof(char*) * (hash_size+1));
char** packed = (char **)mem_sys_allocate_zeroed(sizeof(char*) * (hash_size+1));
INTVAL i = 0;


Expand Down Expand Up @@ -3539,10 +3738,18 @@ inline op nqp_delete_f(out INT, in STR) :base_core {
#endif
}

inline op nqp_gethostname(out STR) {
char hostname[65];
gethostname(hostname, 65);
$1 = Parrot_str_new_init(interp, hostname, strlen(hostname), Parrot_utf8_encoding_ptr, 0);
inline op nqp_spawn(out INT, in PMC, in STR, in PMC) {
STRING *dir = $3;
PMC *env = $4;
char *const *argv = pack_arg_array(interp, $2);
char *args = create_command_line(argv);
STRING *command = Parrot_str_new(interp, args, 0);
STRING * const old_cwd = Parrot_file_getcwd(interp);
Parrot_str_free_cstring(args);

Parrot_file_chdir(interp, dir);
$1 = Run_OS_Command(interp, command, env);
Parrot_file_chdir(interp, old_cwd);
}

inline op nqp_shell(out INT, in STR, in STR, in PMC) {
Expand Down Expand Up @@ -3576,6 +3783,12 @@ inline op nqp_getenvhash(out PMC) {
}
}

inline op nqp_gethostname(out STR) {
char hostname[65];
gethostname(hostname, 65);
$1 = Parrot_str_new_init(interp, hostname, strlen(hostname), Parrot_utf8_encoding_ptr, 0);
}

/*

=item nqp_encode
Expand Down

0 comments on commit 54f6198

Please sign in to comment.