awesome.spawn: Accept tables as arguments

With this change, awesome.spawn() can be called with a table as its command line
argument. This gets rid of lots of problems with escaping the arguments. For
example, the following call is now possible:

  awesome.spawn({ "bash", "-c", "echo \"foo\"" })

Thanks to Ignas Anikevičius for inspiring me to this.

Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
Uli Schlachter 2013-03-10 14:54:58 +01:00
parent 4c3bac07ec
commit 3c40d6b470
2 changed files with 43 additions and 26 deletions

View File

@ -29,7 +29,7 @@ module("awesome")
-- @class function -- @class function
--- Spawn a program. --- Spawn a program.
-- @param cmd The command to launch. -- @param cmd The command to launch. Either a string or a table of strings.
-- @param use_sn Use startup-notification, true or false, default to true. -- @param use_sn Use startup-notification, true or false, default to true.
-- @return Process ID if everything is OK, or an error string if an error occured. -- @return Process ID if everything is OK, or an error string if an error occured.

67
spawn.c
View File

@ -263,29 +263,40 @@ spawn_callback(gpointer user_data)
setsid(); setsid();
} }
/** Spawn a command. /** Parse a command line.
* \param command_line The command line to launch. * \param L The Lua VM state.
* \param error A error pointer to fill with the possible error from * \param idx The index of the argument that we should parse
* g_spawn_async. * \return The argv array for the new process.
* \return g_spawn_async value.
*/ */
static GPid static gchar **
spawn_command(const gchar *command_line, GError **error) parse_command(lua_State *L, int idx)
{ {
gboolean retval; gchar **argv = NULL;
GPid pid;
gchar **argv = 0;
if(!g_shell_parse_argv(command_line, NULL, &argv, error)) if (lua_isstring(L, idx))
return 0; {
const char *cmd = luaL_checkstring(L, idx);
if(!g_shell_parse_argv(cmd, NULL, &argv, NULL))
return NULL;
}
else if (lua_istable(L, idx))
{
size_t i, len = luaA_rawlen(L, idx);
argv = g_new0(gchar *, len + 1);
retval = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, for (i = 0; i < len; i++)
spawn_callback, NULL, &pid, error); {
g_strfreev (argv); lua_rawgeti(L, idx, i+1);
argv[i] = g_strdup(lua_tostring(L, -1));
lua_pop(L, 1);
}
}
else
{
luaL_error(L, "Invalid argument to spawn(), expect string or table");
}
if (!retval) return argv;
return 0;
return pid;
} }
/** Spawn a program. /** Spawn a program.
@ -301,27 +312,31 @@ spawn_command(const gchar *command_line, GError **error)
int int
luaA_spawn(lua_State *L) luaA_spawn(lua_State *L)
{ {
const char *cmd; gchar **argv = NULL;
bool use_sn = true; bool use_sn = true;
gboolean retval;
GPid pid;
if(lua_gettop(L) >= 2) if(lua_gettop(L) >= 2)
use_sn = luaA_checkboolean(L, 2); use_sn = luaA_checkboolean(L, 2);
cmd = luaL_checkstring(L, 1); argv = parse_command(L, 1);
if(!argv)
return 0;
SnLauncherContext *context = NULL; SnLauncherContext *context = NULL;
if(use_sn) if(use_sn)
{ {
char *cmdname, *space; char *cmdname, *space;
const char *first_no_space_char = cmd; const char *first_no_space_char = argv[0];
/* Look for the first char which is not space */ /* Look for the first char which is not space */
while(*first_no_space_char && *first_no_space_char == ' ') while(*first_no_space_char && *first_no_space_char == ' ')
first_no_space_char++; first_no_space_char++;
/* Look for space in the string to get the command name. */ /* Look for space in the string to get the command name. */
if((space = strchr(first_no_space_char, ' '))) if((space = strchr(first_no_space_char, ' ')))
cmdname = a_strndup(cmd, space - cmd); cmdname = a_strndup(argv[0], space - argv[0]);
else else
cmdname = a_strdup(cmd); cmdname = a_strdup(argv[0]);
context = sn_launcher_context_new(globalconf.sndisplay, globalconf.default_screen); context = sn_launcher_context_new(globalconf.sndisplay, globalconf.default_screen);
sn_launcher_context_set_name(context, "awesome"); sn_launcher_context_set_name(context, "awesome");
@ -337,8 +352,10 @@ luaA_spawn(lua_State *L)
} }
GError *error = NULL; GError *error = NULL;
GPid pid = spawn_command(cmd, &error); retval = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
if(!pid) spawn_callback, NULL, &pid, &error);
g_strfreev(argv);
if(!retval)
{ {
/* push error on stack */ /* push error on stack */
lua_pushstring(L, error->message); lua_pushstring(L, error->message);