rktio: add ShellExecute

This commit is contained in:
Matthew Flatt 2017-06-20 20:57:32 -06:00
parent d70cb9aec0
commit 898b9ffe10
8 changed files with 165 additions and 73 deletions

View File

@ -296,7 +296,7 @@ Scheme_Config *scheme_init_error_escape_proc(Scheme_Config *config)
*/ */
static intptr_t sch_vsprintf(char *s, intptr_t maxlen, const char *msg, va_list args, char **_s, static intptr_t sch_vsprintf(char *s, intptr_t maxlen, const char *msg, va_list args, char **_s,
Scheme_Object **_errno_val) Scheme_Object **_errno_val, int *_unsupported)
/* NULL for s means allocate the buffer here (and return in (_s), but this function /* NULL for s means allocate the buffer here (and return in (_s), but this function
doesn't allocate before extracting arguments from the stack. */ doesn't allocate before extracting arguments from the stack. */
{ {
@ -532,6 +532,10 @@ static intptr_t sch_vsprintf(char *s, intptr_t maxlen, const char *msg, va_list
*_errno_val = err_kind; *_errno_val = err_kind;
} }
} }
if (_unsupported
&& (errid == RKTIO_ERROR_UNSUPPORTED)
&& (errkind == RKTIO_ERROR_KIND_RACKET))
*_unsupported = 1;
} }
break; break;
case 'e': case 'e':
@ -758,7 +762,7 @@ static intptr_t scheme_sprintf(char *s, intptr_t maxlen, const char *msg, ...)
GC_CAN_IGNORE va_list args; GC_CAN_IGNORE va_list args;
HIDE_FROM_XFORM(va_start(args, msg)); HIDE_FROM_XFORM(va_start(args, msg));
len = sch_vsprintf(s, maxlen, msg, args, NULL, NULL); len = sch_vsprintf(s, maxlen, msg, args, NULL, NULL, NULL);
HIDE_FROM_XFORM(va_end(args)); HIDE_FROM_XFORM(va_end(args));
return len; return len;
@ -1074,7 +1078,7 @@ scheme_signal_error (const char *msg, ...)
intptr_t len; intptr_t len;
HIDE_FROM_XFORM(va_start(args, msg)); HIDE_FROM_XFORM(va_start(args, msg));
len = sch_vsprintf(NULL, 0, msg, args, &buffer, NULL); len = sch_vsprintf(NULL, 0, msg, args, &buffer, NULL, NULL);
HIDE_FROM_XFORM(va_end(args)); HIDE_FROM_XFORM(va_end(args));
if (scheme_current_thread->current_local_env) { if (scheme_current_thread->current_local_env) {
@ -1106,7 +1110,7 @@ void scheme_warning(char *msg, ...)
intptr_t len; intptr_t len;
HIDE_FROM_XFORM(va_start(args, msg)); HIDE_FROM_XFORM(va_start(args, msg));
len = sch_vsprintf(NULL, 0, msg, args, &buffer, NULL); len = sch_vsprintf(NULL, 0, msg, args, &buffer, NULL, NULL);
HIDE_FROM_XFORM(va_end(args)); HIDE_FROM_XFORM(va_end(args));
buffer[len++] = '\n'; buffer[len++] = '\n';
@ -1130,7 +1134,7 @@ void scheme_log(Scheme_Logger *logger, int level, int flags,
} }
HIDE_FROM_XFORM(va_start(args, msg)); HIDE_FROM_XFORM(va_start(args, msg));
len = sch_vsprintf(NULL, 0, msg, args, &buffer, NULL); len = sch_vsprintf(NULL, 0, msg, args, &buffer, NULL, NULL);
HIDE_FROM_XFORM(va_end(args)); HIDE_FROM_XFORM(va_end(args));
buffer[len] = 0; buffer[len] = 0;
@ -1153,7 +1157,7 @@ void scheme_log_w_data(Scheme_Logger *logger, int level, int flags,
} }
HIDE_FROM_XFORM(va_start(args, msg)); HIDE_FROM_XFORM(va_start(args, msg));
len = sch_vsprintf(NULL, 0, msg, args, &buffer, NULL); len = sch_vsprintf(NULL, 0, msg, args, &buffer, NULL, NULL);
HIDE_FROM_XFORM(va_end(args)); HIDE_FROM_XFORM(va_end(args));
buffer[len] = 0; buffer[len] = 0;
@ -2227,7 +2231,7 @@ void scheme_read_err(Scheme_Object *port,
Scheme_Object *loc; Scheme_Object *loc;
HIDE_FROM_XFORM(va_start(args, detail)); HIDE_FROM_XFORM(va_start(args, detail));
slen = sch_vsprintf(NULL, 0, detail, args, &s, NULL); slen = sch_vsprintf(NULL, 0, detail, args, &s, NULL, NULL);
HIDE_FROM_XFORM(va_end(args)); HIDE_FROM_XFORM(va_end(args));
ls = ""; ls = "";
@ -2323,7 +2327,7 @@ Scheme_Object *scheme_numr_err(Scheme_Object *complain,
intptr_t slen; intptr_t slen;
HIDE_FROM_XFORM(va_start(args, detail)); HIDE_FROM_XFORM(va_start(args, detail));
slen = sch_vsprintf(NULL, 0, detail, args, &s, NULL); slen = sch_vsprintf(NULL, 0, detail, args, &s, NULL, NULL);
HIDE_FROM_XFORM(va_end(args)); HIDE_FROM_XFORM(va_end(args));
if (SCHEME_FALSEP(complain)) if (SCHEME_FALSEP(complain))
@ -2519,7 +2523,7 @@ void scheme_wrong_syntax(const char *where,
GC_CAN_IGNORE va_list args; GC_CAN_IGNORE va_list args;
HIDE_FROM_XFORM(va_start(args, detail)); HIDE_FROM_XFORM(va_start(args, detail));
slen = sch_vsprintf(NULL, 0, detail, args, &s, NULL); slen = sch_vsprintf(NULL, 0, detail, args, &s, NULL, NULL);
HIDE_FROM_XFORM(va_end(args)); HIDE_FROM_XFORM(va_end(args));
} }
@ -2536,7 +2540,7 @@ void scheme_unbound_syntax(const char *where,
GC_CAN_IGNORE va_list args; GC_CAN_IGNORE va_list args;
HIDE_FROM_XFORM(va_start(args, detail)); HIDE_FROM_XFORM(va_start(args, detail));
slen = sch_vsprintf(NULL, 0, detail, args, &s, NULL); slen = sch_vsprintf(NULL, 0, detail, args, &s, NULL, NULL);
HIDE_FROM_XFORM(va_end(args)); HIDE_FROM_XFORM(va_end(args));
do_wrong_syntax(where, detail_form, form, s, slen, scheme_null, MZEXN_FAIL_SYNTAX_UNBOUND); do_wrong_syntax(where, detail_form, form, s, slen, scheme_null, MZEXN_FAIL_SYNTAX_UNBOUND);
@ -2558,7 +2562,7 @@ void scheme_wrong_syntax_with_more_sources(const char *where,
GC_CAN_IGNORE va_list args; GC_CAN_IGNORE va_list args;
HIDE_FROM_XFORM(va_start(args, detail)); HIDE_FROM_XFORM(va_start(args, detail));
slen = sch_vsprintf(NULL, 0, detail, args, &s, NULL); slen = sch_vsprintf(NULL, 0, detail, args, &s, NULL, NULL);
HIDE_FROM_XFORM(va_end(args)); HIDE_FROM_XFORM(va_end(args));
} }
@ -2603,7 +2607,7 @@ void scheme_wrong_return_arity(const char *where,
GC_CAN_IGNORE va_list args; GC_CAN_IGNORE va_list args;
HIDE_FROM_XFORM(va_start(args, detail)); HIDE_FROM_XFORM(va_start(args, detail));
slen = sch_vsprintf(NULL, 0, detail, args, &s, NULL); slen = sch_vsprintf(NULL, 0, detail, args, &s, NULL, NULL);
HIDE_FROM_XFORM(va_end(args)); HIDE_FROM_XFORM(va_end(args));
} }
@ -2662,7 +2666,7 @@ void scheme_raise_out_of_memory(const char *where, const char *msg, ...)
GC_CAN_IGNORE va_list args; GC_CAN_IGNORE va_list args;
HIDE_FROM_XFORM(va_start(args, msg)); HIDE_FROM_XFORM(va_start(args, msg));
slen = sch_vsprintf(NULL, 0, msg, args, &s, NULL); slen = sch_vsprintf(NULL, 0, msg, args, &s, NULL, NULL);
HIDE_FROM_XFORM(va_end(args)); HIDE_FROM_XFORM(va_end(args));
} }
@ -4528,7 +4532,7 @@ scheme_raise_exn(int id, ...)
GC_CAN_IGNORE va_list args; GC_CAN_IGNORE va_list args;
intptr_t alen; intptr_t alen;
char *msg; char *msg;
int i, c; int i, c, unsupported = 0;
Scheme_Object *eargs[MZEXN_MAXARGS], *errno_val = NULL; Scheme_Object *eargs[MZEXN_MAXARGS], *errno_val = NULL;
char *buffer; char *buffer;
@ -4548,7 +4552,7 @@ scheme_raise_exn(int id, ...)
msg = mzVA_ARG(args, char*); msg = mzVA_ARG(args, char*);
alen = sch_vsprintf(NULL, 0, msg, args, &buffer, &errno_val); alen = sch_vsprintf(NULL, 0, msg, args, &buffer, &errno_val, &unsupported);
HIDE_FROM_XFORM(va_end(args)); HIDE_FROM_XFORM(va_end(args));
#ifndef NO_SCHEME_EXNS #ifndef NO_SCHEME_EXNS
@ -4564,6 +4568,9 @@ scheme_raise_exn(int id, ...)
eargs[2] = errno_val; eargs[2] = errno_val;
c++; c++;
} }
} else if (unsupported) {
if (id == MZEXN_FAIL)
id = MZEXN_FAIL_UNSUPPORTED;
} }
do_raise(scheme_make_struct_instance(exn_table[id].type, do_raise(scheme_make_struct_instance(exn_table[id].type,

View File

@ -6188,13 +6188,10 @@ static Scheme_Object *subprocess(int c, Scheme_Object *args[])
static Scheme_Object *sch_shell_execute(int c, Scheme_Object *argv[]) static Scheme_Object *sch_shell_execute(int c, Scheme_Object *argv[])
{ {
#ifdef WINDOWS_PROCESSES
char *dir; char *dir;
int show = 0; int show = 0;
# define mzseSHOW(s, x) s = x int nplen;
#else Scheme_Object *sv, *sf, *sp;
# define mzseSHOW(s, x) /* empty */
#endif
if (!SCHEME_FALSEP(argv[0]) && !SCHEME_CHAR_STRINGP(argv[0])) if (!SCHEME_FALSEP(argv[0]) && !SCHEME_CHAR_STRINGP(argv[0]))
scheme_wrong_contract("shell-execute", "(or/c string? #f)", 0, c, argv); scheme_wrong_contract("shell-execute", "(or/c string? #f)", 0, c, argv);
@ -6209,7 +6206,7 @@ static Scheme_Object *sch_shell_execute(int c, Scheme_Object *argv[])
# define mzseCMP(id, str) \ # define mzseCMP(id, str) \
if (SAME_OBJ(scheme_intern_symbol(str), argv[4]) \ if (SAME_OBJ(scheme_intern_symbol(str), argv[4]) \
|| SAME_OBJ(scheme_intern_symbol(# id), argv[4])) { \ || SAME_OBJ(scheme_intern_symbol(# id), argv[4])) { \
mzseSHOW(show, id); show_set = 1; } show = RKTIO_ ## id; show_set = 1; }
mzseCMP(SW_HIDE, "sw_hide"); mzseCMP(SW_HIDE, "sw_hide");
mzseCMP(SW_MAXIMIZE, "sw_maximize"); mzseCMP(SW_MAXIMIZE, "sw_maximize");
mzseCMP(SW_MINIMIZE, "sw_minimize"); mzseCMP(SW_MINIMIZE, "sw_minimize");
@ -6227,61 +6224,35 @@ static Scheme_Object *sch_shell_execute(int c, Scheme_Object *argv[])
scheme_wrong_type("shell-execute", "show-mode symbol", 4, c, argv); scheme_wrong_type("shell-execute", "show-mode symbol", 4, c, argv);
} }
#ifdef WINDOWS_PROCESSES dir = scheme_expand_string_filename(argv[3],
dir =
#endif
scheme_expand_string_filename(argv[3],
"shell-execute", NULL, "shell-execute", NULL,
SCHEME_GUARD_FILE_EXISTS); SCHEME_GUARD_FILE_EXISTS);
#ifdef WINDOWS_PROCESSES
{
SHELLEXECUTEINFOW se;
int nplen;
Scheme_Object *sv, *sf, *sp;
nplen = strlen(dir); nplen = strlen(dir);
dir = scheme_normal_path_seps(dir, &nplen, 0); dir = scheme_normal_path_seps(dir, &nplen, 0);
if (SCHEME_FALSEP(argv[0])) if (SCHEME_FALSEP(argv[0]))
sv = scheme_false; sv = NULL;
else else
sv = scheme_char_string_to_byte_string(argv[0]); sv = scheme_char_string_to_byte_string(argv[0]);
sf = scheme_char_string_to_byte_string(argv[1]); sf = scheme_char_string_to_byte_string(argv[1]);
sp = scheme_char_string_to_byte_string(argv[2]); sp = scheme_char_string_to_byte_string(argv[2]);
memset(&se, 0, sizeof(se)); if (rktio_shell_execute(scheme_rktio,
se.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_DDEWAIT; sv ? SCHEME_BYTE_STR_VAL(sv) : NULL,
se.cbSize = sizeof(se); SCHEME_BYTE_STR_VAL(sf),
if (SCHEME_FALSEP(sv)) SCHEME_BYTE_STR_VAL(sp),
se.lpVerb = NULL; dir,
else { show))
se.lpVerb = WIDE_PATH_COPY(SCHEME_BYTE_STR_VAL(sv));
}
se.lpFile = WIDE_PATH_COPY(SCHEME_BYTE_STR_VAL(sf));
se.lpParameters = WIDE_PATH_COPY(SCHEME_BYTE_STR_VAL(sp));
se.lpDirectory = WIDE_PATH_COPY(dir);
se.nShow = show;
se.hwnd = NULL;
/* Used to use ShellExecuteEx(&se) here. Not sure why it doesn't work,
and the problem was intermittent (e.g., worked for opening a URL
with IE as the default browser, but failed with Netscape). */
if (ShellExecuteW(se.hwnd, se.lpVerb, se.lpFile, se.lpParameters, se.lpDirectory, se.nShow)) {
return scheme_false; return scheme_false;
} else { else {
scheme_signal_error("shell-execute: execute failed\n" scheme_raise_exn(MZEXN_FAIL,
"shell-execute: execute failed\n"
" command: %V\n" " command: %V\n"
" system error: %E", " system error: %R",
argv[1], argv[1]);
GetLastError());
return NULL; return NULL;
} }
}
#else
scheme_raise_exn(MZEXN_FAIL_UNSUPPORTED,
"shell-execute: " NOT_SUPPORTED_STR);
return NULL;
#endif
} }
/*========================================================================*/ /*========================================================================*/

View File

@ -25,6 +25,7 @@ OBJS = rktio_fs.@LTO@ \
rktio_envvars.@LTO@ \ rktio_envvars.@LTO@ \
rktio_fs_change.@LTO@ \ rktio_fs_change.@LTO@ \
rktio_flock.@LTO@ \ rktio_flock.@LTO@ \
rktio_shellex.@LTO@ \
rktio_time.@LTO@ \ rktio_time.@LTO@ \
rktio_error.@LTO@ \ rktio_error.@LTO@ \
rktio_hash.@LTO@ \ rktio_hash.@LTO@ \
@ -74,6 +75,9 @@ rktio_fs_change.@LTO@: $(srcdir)/rktio_fs_change.c $(RKTIO_HEADERS)
rktio_flock.@LTO@: $(srcdir)/rktio_flock.c $(RKTIO_HEADERS) rktio_flock.@LTO@: $(srcdir)/rktio_flock.c $(RKTIO_HEADERS)
$(CC) $(CFLAGS) -I$(srcdir) -I. -o rktio_flock.@LTO@ -c $(srcdir)/rktio_flock.c $(CC) $(CFLAGS) -I$(srcdir) -I. -o rktio_flock.@LTO@ -c $(srcdir)/rktio_flock.c
rktio_shellex.@LTO@: $(srcdir)/rktio_shellex.c $(RKTIO_HEADERS)
$(CC) $(CFLAGS) -I$(srcdir) -I. -o rktio_shellex.@LTO@ -c $(srcdir)/rktio_shellex.c
rktio_time.@LTO@: $(srcdir)/rktio_time.c $(RKTIO_HEADERS) rktio_time.@LTO@: $(srcdir)/rktio_time.c $(RKTIO_HEADERS)
$(CC) $(CFLAGS) -I$(srcdir) -I. -o rktio_time.@LTO@ -c $(srcdir)/rktio_time.c $(CC) $(CFLAGS) -I$(srcdir) -I. -o rktio_time.@LTO@ -c $(srcdir)/rktio_time.c

View File

@ -848,6 +848,33 @@ RKTIO_EXTERN intptr_t rktio_get_process_children_milliseconds(rktio_t *rktio);
RKTIO_EXTERN rktio_timestamp_t rktio_get_seconds(rktio_t *rktio); RKTIO_EXTERN rktio_timestamp_t rktio_get_seconds(rktio_t *rktio);
RKTIO_EXTERN rktio_date_t *rktio_seconds_to_date(rktio_t *rktio, rktio_timestamp_t seconds, int nanoseconds, int get_gmt); RKTIO_EXTERN rktio_date_t *rktio_seconds_to_date(rktio_t *rktio, rktio_timestamp_t seconds, int nanoseconds, int get_gmt);
/*************************************************/
/* Windows ShellExecute */
enum {
RKTIO_SW_HIDE,
RKTIO_SW_MAXIMIZE,
RKTIO_SW_MINIMIZE,
RKTIO_SW_RESTORE,
RKTIO_SW_SHOW,
RKTIO_SW_SHOWDEFAULT,
RKTIO_SW_SHOWMAXIMIZED,
RKTIO_SW_SHOWMINIMIZED,
RKTIO_SW_SHOWMINNOACTIVE,
RKTIO_SW_SHOWNA,
RKTIO_SW_SHOWNOACTIVATE,
RKTIO_SW_SHOWNORMAL
};
rktio_ok_t rktio_shell_execute(rktio_t *rktio,
const char *verb,
const char *target,
const char *arg,
const char *dir,
int show_mode);
/* Support only on Windows to run `ShellExecute`. The `dir` argument
needs to have normalized path separators. */
/*************************************************/ /*************************************************/
/* Errors */ /* Errors */
@ -891,6 +918,7 @@ enum {
RKTIO_ERROR_TRY_AGAIN_WITH_IPV4, /* for TCP listen */ RKTIO_ERROR_TRY_AGAIN_WITH_IPV4, /* for TCP listen */
RKTIO_ERROR_TIME_OUT_OF_RANGE, RKTIO_ERROR_TIME_OUT_OF_RANGE,
RKTIO_ERROR_NO_SUCH_ENVVAR, RKTIO_ERROR_NO_SUCH_ENVVAR,
RKTIO_ERROR_SHELL_EXECUTE_FAILED,
}; };
RKTIO_EXTERN void rktio_set_last_error(rktio_t *rktio, int kind, int errid); RKTIO_EXTERN void rktio_set_last_error(rktio_t *rktio, int kind, int errid);

View File

@ -37,6 +37,7 @@ err_str_t err_strs[] = {
{ RKTIO_ERROR_TRY_AGAIN_WITH_IPV4, "listen failed, but try again with just IPv4 addresses" }, { RKTIO_ERROR_TRY_AGAIN_WITH_IPV4, "listen failed, but try again with just IPv4 addresses" },
{ RKTIO_ERROR_TIME_OUT_OF_RANGE, "time value out-of-range for date conversion" }, { RKTIO_ERROR_TIME_OUT_OF_RANGE, "time value out-of-range for date conversion" },
{ RKTIO_ERROR_NO_SUCH_ENVVAR, "no value as an environment variable" }, { RKTIO_ERROR_NO_SUCH_ENVVAR, "no value as an environment variable" },
{ RKTIO_ERROR_SHELL_EXECUTE_FAILED, "ShellExecute failed" },
{ 0, NULL } { 0, NULL }
}; };

View File

@ -0,0 +1,76 @@
#include "rktio.h"
#include "rktio_private.h"
#define rktio_SHOW_CONVERT(v) case RKTIO_ ## v: show_mode = v; break
rktio_ok_t rktio_shell_execute(rktio_t *rktio,
const char *verb,
const char *target,
const char *arg,
const char *dir,
int show_mode)
/* dir needs to have normalized path separators */
{
#ifdef RKTIO_SYSTEM_WINDOWS
SHELLEXECUTEINFOW se;
int ok, r;
switch(show_mode) {
rktio_SHOW_CONVERT(SW_HIDE);
rktio_SHOW_CONVERT(SW_MAXIMIZE);
rktio_SHOW_CONVERT(SW_MINIMIZE);
rktio_SHOW_CONVERT(SW_RESTORE);
rktio_SHOW_CONVERT(SW_SHOW);
rktio_SHOW_CONVERT(SW_SHOWDEFAULT);
rktio_SHOW_CONVERT(SW_SHOWMAXIMIZED);
rktio_SHOW_CONVERT(SW_SHOWMINIMIZED);
rktio_SHOW_CONVERT(SW_SHOWMINNOACTIVE);
rktio_SHOW_CONVERT(SW_SHOWNA);
rktio_SHOW_CONVERT(SW_SHOWNOACTIVATE);
rktio_SHOW_CONVERT(SW_SHOWNORMAL);
}
memset(&se, 0, sizeof(se));
se.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_DDEWAIT;
se.cbSize = sizeof(se);
if (!verb)
se.lpVerb = NULL;
else
se.lpVerb = WIDE_PATH_copy(verb);
se.lpFile = WIDE_PATH_copy(target);
se.lpParameters = WIDE_PATH_copy(arg);
se.lpDirectory = WIDE_PATH_copy(dir);
se.nShow = show_mode;
se.hwnd = NULL;
r = (int)(intptr_t)ShellExecuteW(se.hwnd, se.lpVerb, se.lpFile, se.lpParameters,
se.lpDirectory, se.nShow);
ok = (r > 32);
if (!ok) {
switch(r) {
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
case ERROR_BAD_FORMAT:
set_windows_error(r);
break;
default:
/* Other results are not Windows error codes,
so just collapse them to a Racket error */
set_racket_error(RKTIO_ERROR_SHELL_EXECUTE_FAILED);
break;
}
}
if (se.lpVerb) free((char *)se.lpVerb);
free((char *)se.lpFile);
free((char *)se.lpParameters);
free((char *)se.lpDirectory);
return ok;
#else
set_racket_error(RKTIO_ERROR_UNSUPPORTED);
return 0;
#endif
}

View File

@ -154,6 +154,10 @@
RelativePath="..\..\rktio\rktio_flock.c" RelativePath="..\..\rktio\rktio_flock.c"
> >
</File> </File>
<File
RelativePath="..\..\rktio\rktio_shellex.c"
>
</File>
<File <File
RelativePath="..\..\rktio\rktio_time.c" RelativePath="..\..\rktio\rktio_time.c"
> >

View File

@ -127,6 +127,7 @@
<ClCompile Include="..\..\rktio\rktio_envvars.c" /> <ClCompile Include="..\..\rktio\rktio_envvars.c" />
<ClCompile Include="..\..\rktio\rktio_fs_change.c" /> <ClCompile Include="..\..\rktio\rktio_fs_change.c" />
<ClCompile Include="..\..\rktio\rktio_flock.c" /> <ClCompile Include="..\..\rktio\rktio_flock.c" />
<ClCompile Include="..\..\rktio\rktio_shellex.c" />
<ClCompile Include="..\..\rktio\rktio_time.c" /> <ClCompile Include="..\..\rktio\rktio_time.c" />
<ClCompile Include="..\..\rktio\rktio_error.c" /> <ClCompile Include="..\..\rktio\rktio_error.c" />
<ClCompile Include="..\..\rktio\rktio_hash.c" /> <ClCompile Include="..\..\rktio\rktio_hash.c" />