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
--- 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.
-- @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();
}
/** Spawn a command.
* \param command_line The command line to launch.
* \param error A error pointer to fill with the possible error from
* g_spawn_async.
* \return g_spawn_async value.
/** Parse a command line.
* \param L The Lua VM state.
* \param idx The index of the argument that we should parse
* \return The argv array for the new process.
*/
static GPid
spawn_command(const gchar *command_line, GError **error)
static gchar **
parse_command(lua_State *L, int idx)
{
gboolean retval;
GPid pid;
gchar **argv = 0;
gchar **argv = NULL;
if(!g_shell_parse_argv(command_line, NULL, &argv, error))
return 0;
if (lua_isstring(L, idx))
{
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,
spawn_callback, NULL, &pid, error);
g_strfreev (argv);
for (i = 0; i < len; i++)
{
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 0;
return pid;
return argv;
}
/** Spawn a program.
@ -301,27 +312,31 @@ spawn_command(const gchar *command_line, GError **error)
int
luaA_spawn(lua_State *L)
{
const char *cmd;
gchar **argv = NULL;
bool use_sn = true;
gboolean retval;
GPid pid;
if(lua_gettop(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;
if(use_sn)
{
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 */
while(*first_no_space_char && *first_no_space_char == ' ')
first_no_space_char++;
/* Look for space in the string to get the command name. */
if((space = strchr(first_no_space_char, ' ')))
cmdname = a_strndup(cmd, space - cmd);
cmdname = a_strndup(argv[0], space - argv[0]);
else
cmdname = a_strdup(cmd);
cmdname = a_strdup(argv[0]);
context = sn_launcher_context_new(globalconf.sndisplay, globalconf.default_screen);
sn_launcher_context_set_name(context, "awesome");
@ -337,8 +352,10 @@ luaA_spawn(lua_State *L)
}
GError *error = NULL;
GPid pid = spawn_command(cmd, &error);
if(!pid)
retval = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
spawn_callback, NULL, &pid, &error);
g_strfreev(argv);
if(!retval)
{
/* push error on stack */
lua_pushstring(L, error->message);