Args auto-parsing, use main.binding
authorMyhailo Danylenko <isbear@ukrpost.net>
Mon, 16 Mar 2009 18:54:16 +0200
changeset 34 8206d7cb1447
parent 33 db5396037b43
child 35 8deda77c72e5
Args auto-parsing, use main.binding
TODO
examples/beep.lua
examples/jobs.lua
examples/marking.lua
examples/mcabberrc.lua
examples/room_priv.lua
examples/xep0004.lua
examples/xep0030.lua
examples/xep0047.lua
examples/xep0060.lua
examples/xep0077.lua
examples/xep0146.lua
examples/xep0163.lua
main.c
--- a/TODO	Mon Mar 16 06:12:55 2009 +0200
+++ b/TODO	Mon Mar 16 18:54:16 2009 +0200
@@ -2,7 +2,7 @@
 print should allow other types to be printed
 finish roster list information
 non-setting settings?
-mcabber_config_file uses option to set dir? eliminate main.config_file, use modules_dir option instead in lua paths?
+mcabber_config_file use option to set dir?
 do uninitialization of commands and features with objects?
 help system accessors (needs major rewrite and planning)
 object access to roster and buddies?
@@ -13,4 +13,7 @@
 think about how tune notification should act on start/quit
 clarify situation with disco, maybe it is due to this we are unable to receive pep events
 lm debug should be more flexible
+common pubsub infrastructure, in particular, xmlns-based event handlers?
+forms abandoning hook to inform server
+eliminate main.parse_args?
 
--- a/examples/beep.lua	Mon Mar 16 06:12:55 2009 +0200
+++ b/examples/beep.lua	Mon Mar 16 18:54:16 2009 +0200
@@ -15,7 +15,7 @@
 			hooks_d['hook-message-in'].beep = nil
 			print ( "Beep on message is disabled" )
 		end
-	end, 'yesno' )
+	end, false, 'yesno' )
 
 commands_help['beep'] = "[enable|disable|on|off|yes|no|true|false]\n\nEnables or disables beeping on all messages.\nIf state is omitted, prints current state."
 
--- a/examples/jobs.lua	Mon Mar 16 06:12:55 2009 +0200
+++ b/examples/jobs.lua	Mon Mar 16 18:54:16 2009 +0200
@@ -27,7 +27,6 @@
 
 main.command ( 'delay',
 	function ( args )
-		args = main.parse_args ( args )
 		local who
 		if args.t then
 			who = args.t
@@ -38,11 +37,10 @@
 		local mess = args[2]
 		delayed_jobs[who] = { }
 		delayed_jobs[who][stat] = 'say_to -q ' .. who .. ' ' .. mess
-	end )
+	end, true )
 
 main.command ( 'job',
 	function ( args )
-		args = main.parse_args ( args )
 		local action, jid, stat = args[1], args[2], args[3]
 		if action == 'del' then
 			delayed_jobs[jid][stat] = nil
@@ -59,7 +57,7 @@
 				print ( 'No jobs' )
 			end
 		end
-	end, { "del" } )
+	end, true, { "del" } )
 
 commands_help['delay'] = "[-t target_jid] status_letter message\n\nDelays sending a message to target jid (or current buddy) until it switches to specified status."
 commands_help['job'] = "[del jid status_letter]\n\nLists available jobs or deletes specified one."
--- a/examples/marking.lua	Mon Mar 16 06:12:55 2009 +0200
+++ b/examples/marking.lua	Mon Mar 16 18:54:16 2009 +0200
@@ -44,11 +44,11 @@
 
 main.command ( 'marked',
 	function ( args )
-		local cmd = args:match ( "^%s*(%w+)" )
+		local cmd = args[1]
 		if cmd == 'clear' then
 			marked_clear ()
 		elseif cmd == 'do' then
-			local command = args:match ( "^%s*%w+%s+(.+)" )
+			local command = args[2]
 			foreach_marked (
 				function ( jid )
 					main.run ( string.format ( command, jid ) )
@@ -60,17 +60,23 @@
 					print ( ' - ' .. jid )
 				end )
 		end
-	end, { 'clear', 'do' } )
+	end, true, { 'clear', 'do' } )
 
 commands_help['marked'] = "[clear | do mcabber_command]\n\nOperates on marked buddies. Without arguments prints list of marked jids.\nCommand should contain %s in place, where jid should be inserted."
 
+mark_ins_bound = false
+
 -- Ins
--- TODO: check if ins already bound
-main.run ( 'bind 331 = lua mark_toggle ()' )
+if not main.binding ( '331' ) then
+	main.binding ( '331', 'lua mark_toggle ()' )
+	mark_ins_bound = true
+end
 
 hooks_d['hook-quit'].mark =
 	function ( args )
-		main.run ( 'bind 331 =' )
+		if mark_ins_bound then
+			main.binding ( '331', nil )
+		end
 	end
 
 -- vim: se ts=4: --
--- a/examples/mcabberrc.lua	Mon Mar 16 06:12:55 2009 +0200
+++ b/examples/mcabberrc.lua	Mon Mar 16 18:54:16 2009 +0200
@@ -132,7 +132,7 @@
 main.command ( 'post',
 	function ( args )
 		main.run ( 'say_to -f ' .. args .. ' .' )
-	end, 'filename' )
+	end, false, 'filename' )
 main.command ( 'cmd',
 	function ( args )
 		local to = main.current_buddy ()
@@ -146,7 +146,7 @@
 					return false
 				end
 			end )
-	end, 'filename' )
+	end, false, 'filename' )
 main.command ( 'exthelp',
 	function ( args )
 		if commands_help[args] then
@@ -160,7 +160,7 @@
 			print ( list:sub ( 1, -3 ) )
 			print ( "For built-in mcabber commands see /help" )
 		end
-	end, 'cmd' )
+	end, false, 'cmd' )
 main.command ( 'join!',
 	function ( args )
 		main.run ( 'room join ' .. main.current_buddy () )
--- a/examples/room_priv.lua	Mon Mar 16 06:12:55 2009 +0200
+++ b/examples/room_priv.lua	Mon Mar 16 18:54:16 2009 +0200
@@ -4,7 +4,7 @@
 room_cid = main.command ( 'priv',
 	function ( args )
 		main.run ( 'room privmsg ' .. args )
-	end, {} )
+	end, false, {} )
 
 commands_help['priv'] = "nick message\n\nSends private message to room participant. Nick completion available! ;)"
 
@@ -29,13 +29,15 @@
 	end
 end
 
-main.run ( 'bind 338 = lua main.run ( "roster down" ); register_nicks ()' )
-main.run ( 'bind 339 = lua main.run ( "roster up" ); register_nicks ()' )
+room_priv_pgup_action = main.binding ( '338' )
+room_priv_pgdn_action = main.binding ( '339' )
+main.binding ( '338', ('lua main.run ( %q ); register_nicks ()'):format ( room_priv_pgup_action ) )
+main.binding ( '339', ('lua main.run ( %q ); register_nicks ()'):format ( room_priv_pgdn_action ) )
 
 hooks_d['hook-quit'].room_priv =
 	function ( args )
-		main.run ( 'bind 338 = roster down' )
-		main.run ( 'bind 339 = roster up' )
+		main.binding ( '338', room_priv_pgup_action )
+		main.binding ( '339', room_priv_pgdn_action )
 	end
 
 -- vim: se ts=4: --
--- a/examples/xep0004.lua	Mon Mar 16 06:12:55 2009 +0200
+++ b/examples/xep0004.lua	Mon Mar 16 18:54:16 2009 +0200
@@ -107,7 +107,6 @@
 
 main.command ( 'form',
 	function ( args )
-		args = main.parse_args ( args )
 		local action = args[1]
 		local id = tonumber (args[1])
 		if forms[id] then
@@ -154,7 +153,7 @@
 				print ( 'No forms' )
 			end
 		end
-	end, form_cid )
+	end, true, form_cid )
 
 commands_help['form'] = "[del form_id | send form_id | form_id [field_name {clear | [set] value}]\n\nWith bare form id prints info on that form.\nWith field name sets or clears field value. Set subcommand is optional, just to allow values, starting with 'clear'.\nWithout arguments prints form list."
 
--- a/examples/xep0030.lua	Mon Mar 16 06:12:55 2009 +0200
+++ b/examples/xep0030.lua	Mon Mar 16 18:54:16 2009 +0200
@@ -55,7 +55,6 @@
 
 main.command ( 'disco',
 	function ( args )
-		args = main.parse_args ( args )
 		local who
 		if args.t then
 			who = args.t
@@ -108,7 +107,7 @@
 					end
 				end, who )
 		end
-	end, 'jid' )
+	end, true, 'jid' )
 
 commands_help['disco'] = "[-t target_jid] [info | items] [node]\n\nService discovery request.\nInfo is sent if omitted.\nIf info reveals, that buddy can do items, items request also will be sent."
 
--- a/examples/xep0047.lua	Mon Mar 16 06:12:55 2009 +0200
+++ b/examples/xep0047.lua	Mon Mar 16 18:54:16 2009 +0200
@@ -167,7 +167,6 @@
 
 main.command ( 'ibb',
 	function ( args )
-		args = main.parse_args ( args )
 		local action = args[1]
 		if action == 'send' then
 			local who
@@ -201,7 +200,7 @@
 				print ( 'No streams' )
 			end
 		end
-	end, { "send", "accept", "reject", "del" } )
+	end, true, { "send", "accept", "reject", "del" } )
 
 commands_help['ibb'] = "[[-t target_jid] send filename | accept sid filename | reject sid filename | del sid]\n\nRequests, accepts or rejects sending file via in-band bytestream."
 
--- a/examples/xep0060.lua	Mon Mar 16 06:12:55 2009 +0200
+++ b/examples/xep0060.lua	Mon Mar 16 18:54:16 2009 +0200
@@ -306,7 +306,6 @@
 
 main.command ( 'node',
 	function ( args )
-		args = main.parse_args ( args )
 		local who, action, node = args.t, args[1], args[2]
 		if not who then
 			who = main.current_buddy ()
@@ -335,7 +334,7 @@
 		else
 			print ( 'Error: unknown action' )
 		end
-	end )
+	end, true )
 
 -- FIXME
 commands_help['node']           = "[-t jid] [-m max_items] action [node_name]\n\nAction can be subscribe, unsubscribe, retrieve (items, get), create (new), delete (del), purge (del_items), configure (conf*), subscriptions (subscribers), subscription (modify?)"
--- a/examples/xep0077.lua	Mon Mar 16 06:12:55 2009 +0200
+++ b/examples/xep0077.lua	Mon Mar 16 18:54:16 2009 +0200
@@ -162,7 +162,7 @@
 			who = main.full_jid ()
 		end
 		register_to ( who )
-	end, 'jid' )
+	end, false, 'jid' )
 
 main.command ( 'cancel',
 	function ( args )
@@ -173,7 +173,7 @@
 			who = main.full_jid ()
 		end
 		unregister_from ( who )
-	end, 'jid' )
+	end, false, 'jid' )
 
 commands_help['register'] = "[jid]\n\nSends registration request to jid (or current buddy). You, probably, then will need to fill and send some form."
 commands_help['cancel'] = "[jid]\n\nSends registration cancellation request to jid (or current buddy). May require a form filling."
--- a/examples/xep0146.lua	Mon Mar 16 06:12:55 2009 +0200
+++ b/examples/xep0146.lua	Mon Mar 16 18:54:16 2009 +0200
@@ -41,7 +41,6 @@
 
 main.command ( 'remote',
 	function ( args )
-		args = main.parse_args ( args )
 		local who
 		if args.t then
 			who = args.t
@@ -69,7 +68,7 @@
 		elseif action then
 			remote_command ( who, action )
 		end
-	end, 'jid' )
+	end, true, 'jid' )
 
 commands_help['remote'] = "[-t target_jid] [remote_command]\n\nPrints list of available remote command or requests execution of specified command."
 
--- a/examples/xep0163.lua	Mon Mar 16 06:12:55 2009 +0200
+++ b/examples/xep0163.lua	Mon Mar 16 18:54:16 2009 +0200
@@ -266,10 +266,9 @@
 		else
 			enable_tune ( enable )
 		end
-	end, 'yesno' )
+	end, false, 'yesno' )
 main.command ( 'mood',
 	function ( args )
-		args = main.parse_args ( args )
 		local data = { }
 		local mood, text = args[1], args[2]
 		if text then
@@ -279,10 +278,9 @@
 			data[mood] = { }
 		end
 		pep_publish ( 'mood', data )
-	end )
+	end, true )
 main.command ( 'activity',
 	function ( args )
-		args = main.parse_args ( args )
 		local data = { }
 		local activity, text = args[1], args[2]
 		if text then
@@ -299,16 +297,15 @@
 			end
 		end
 		pep_publish ( 'activity', data )
-	end )
+	end, true )
 main.command ( 'location',
 	function ( args )
-		args = main.parse_args ( args )
 		local data = { }
 		for key, val in pairs ( args ) do
 			data[key] = { val }
 		end
 		pep_publish ( 'geoloc', data )
-	end )
+	end, true )
 
 commands_help['tune']     = "[enable|disable|on|off|yes|no|true|false]\n\nEnables or disables publishing of notifications about playing music in your player (currently only mpd is supported)."
 commands_help['mood']     = "[mood [message]]\n\nPublishes your mood.\nNote, that for now it does not checks for mood validity, so, see xep0107 for valid moods."
--- a/main.c	Mon Mar 16 06:12:55 2009 +0200
+++ b/main.c	Mon Mar 16 18:54:16 2009 +0200
@@ -559,6 +559,7 @@
 
 typedef struct {
 	int        reference;
+	int        parse_args;
 	lua_State *L;
 } lua_command_callback_t;
 
@@ -566,21 +567,8 @@
 
 static GSList *lua_added_categories = NULL;
 
-/// command function
-/// Function to handle newly registered command.
-/// A: string (arguments)
-static void lua_main_command_handler (char *args, lua_command_callback_t *cb)
-{
-	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
-	lua_pushstring (cb->L, args);
-	if (lua_pcall (cb->L, 1, 0, 0)) {
-		scr_LogPrint (LPRINT_LOGNORM, "lua: Command execution error: %s", lua_tostring (cb->L, -1));
-		lua_pop (cb->L, 1);
-	}
-}
-
-/// main.parse_args
-/// Function to parse command argument string.
+// returns true if string contains errors - unclosed quotes or unvalued option
+/// command arguments table
 /// It can parse barewords (with escapes), double-quoted strings (with escapes), single-quoted strings (without escapes), options and arguments.
 /// Arguments are separated only by whitespace, so, consequential quoted strings or barewords are one argument.
 /// This strings are equal:
@@ -594,12 +582,9 @@
 /// * All options should be before any arguments. First non-option argument disables options recognizing.
 /// * EOL is a cutting edge, that can cut much earlier, than you expect. Non-closed quoted strings lose leading quote and option without value loses its leading minus.
 /// * Escape character just before EOL is preserved.
-/// A: string
-/// R: table
-static int lua_main_parse_args (lua_State *L)
+static int luaL_pushargs (lua_State *L, const char *args)
 {
-	const char  *args = luaL_checkstring (L, 1);
-	const char  *p    = args;
+	const char  *p       = args;
 	luaL_Buffer  buf;
 	int          option  = 0;
 	int          options = 1;
@@ -629,6 +614,8 @@
 			luaL_addlstring (&buf, start, p - start); // XXX: eats quote on eol
 			if (*p)
 				++p;
+			else
+				return 1;
 		} else if (*p == '\'') { // no-escape quote
 			const char *start = ++p;
 			while (*p && *p != '\'')
@@ -636,6 +623,8 @@
 			luaL_addlstring (&buf, start, p - start); // XXX: eats quote on eol
 			if (*p)
 				++p;
+			else
+				return 1;
 		} else { // bareword
 			const char *start = p;
 			while (*p) {
@@ -679,9 +668,41 @@
 			luaL_buffinit (L, &buf);
 		}
 	}
-	if (option)
+
+	if (option) {
 		luaL_ref (L, -2); // XXX: eats minus on eol
+		return 1;
+	}
 
+	return 0;
+}
+
+/// command function
+/// Function to handle newly registered command.
+/// Argument type passed depends on how command is registered.
+/// A: string (arguments) or command arguments table
+static void lua_main_command_handler (char *args, lua_command_callback_t *cb)
+{
+	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+
+	if (cb->parse_args)
+		luaL_pushargs (cb->L, args);
+	else
+		lua_pushstring (cb->L, args);
+
+	if (lua_pcall (cb->L, 1, 0, 0)) {
+		scr_LogPrint (LPRINT_LOGNORM, "lua: Command execution error: %s", lua_tostring (cb->L, -1));
+		lua_pop (cb->L, 1);
+	}
+}
+
+/// main.parse_args
+/// Function to parse command argument string to command arguments table.
+/// A: string
+/// R: table
+static int lua_main_parse_args (lua_State *L)
+{
+	luaL_pushargs (L, luaL_checkstring (L, 1));
 	return 1;
 }
 
@@ -692,6 +713,7 @@
 static int lua_main_add_category (lua_State *L)
 {
 	guint cid;
+
 	if (lua_gettop (L) > 0) {
 		luaL_argcheck (L, lua_type (L, 1) == LUA_TTABLE, 1, "table expected");
 		cid = compl_new_category ();
@@ -708,11 +730,13 @@
 		}
 	} else
 		cid = compl_new_category ();
+
 	if (cid) {
 		lua_added_categories = g_slist_prepend (lua_added_categories, (gpointer) cid);
 		lua_pushinteger (L, cid);
 	} else
 		lua_pushnil (L);
+
 	return 1;
 }
 
@@ -756,37 +780,44 @@
 /// You can also specify a string name (see completion type) instead of table, for non-builtin, you can just pass integer id.
 /// Note, that for now there are no way to unregister completion group, so, resources can be exausted easily.
 /// Also note, that it ignores keys in a completion table.
-/// A: string (command name), command function (optional), table (completions, optional)/completion type (or integer comletion group id, optional)
+/// A: string (command name), command function (optional), boolean (parse args flag, optional), table (completions, optional)/completion type (or integer comletion group id, optional)
 /// R: completion type (integer completion group id or string for builtin types, optional)
 static int lua_main_command (lua_State *L)
-{
+{ // FIXME FIXME
 	const char             *name = luaL_checkstring (L, 1); // XXX: to_utf8? looks like no :/
 	lua_command_callback_t *cb;
-	if (lua_gettop (L) > 1) { // Register
+	int                     top  = lua_gettop (L);
+	if (top > 1) { // Register
 		guint cid = 0;
+		int parse = 0;
 		luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
 
-		if (lua_gettop (L) > 2) { // Completions provided
-			if (lua_type (L, 3) == LUA_TTABLE) {
-				cid = compl_new_category ();
-				if (cid) {
-					lua_added_categories = g_slist_prepend (lua_added_categories, (gpointer) cid);
-					lua_pushnil (L);
-					while (lua_next (L, 3)) {
-						char *word = to_utf8 (luaL_checkstring (L, -1));
-						compl_add_category_word (cid, word);
-						lua_pop (L, 1);
-						g_free (word);
+		if (top > 2) { // parse flag provided
+			parse = lua_toboolean (L, 3);
+
+			if (top > 3) { // Completions provided
+				if (lua_type (L, 4) == LUA_TTABLE) {
+					cid = compl_new_category ();
+					if (cid) {
+						lua_added_categories = g_slist_prepend (lua_added_categories, (gpointer) cid);
+						lua_pushnil (L);
+						while (lua_next (L, 4)) {
+							char *word = to_utf8 (luaL_checkstring (L, -1));
+							compl_add_category_word (cid, word);
+							lua_pop (L, 1);
+							g_free (word);
+						}
 					}
-				}
-			} else
-				cid = luaL_checkenum (L, 3, lua_completion_type);
+				} else
+					cid = luaL_checkenum (L, 4, lua_completion_type);
+			}
 		}
 
 		cb = luaL_malloc (L, sizeof (lua_command_callback_t));
 		lua_pushvalue (L, 2);
-		cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
-		cb->L         = L;
+		cb->reference  = luaL_ref (L, LUA_REGISTRYINDEX);
+		cb->parse_args = parse;
+		cb->L          = L;
 		cmd_add (name, "", cid, 0, (void (*) (char *p)) lua_main_command_handler, cb);
 
 		lua_added_commands = g_slist_prepend (lua_added_commands, g_strdup (name));