Full C args parsing
authorMyhailo Danylenko <isbear@ukrpost.net>
Mon, 16 Mar 2009 00:47:44 +0200
changeset 27 92b254b64360
parent 26 fc83934f9b8d
child 28 90e52372b595
Full C args parsing
TODO
examples/jobs.lua
examples/mcabberrc.lua
examples/xep0004.lua
examples/xep0030.lua
examples/xep0047.lua
examples/xep0060.lua
examples/xep0146.lua
examples/xep0163.lua
main.c
--- a/TODO	Sun Mar 15 23:57:49 2009 +0200
+++ b/TODO	Mon Mar 16 00:47:44 2009 +0200
@@ -9,8 +9,8 @@
 in mcabber add hook/call to also handle room status changes
 well, so, mcabber will pass all arguments to hooks in utf. but do we really need to convert names to utf?
 use glib filename charset conversion functions?
-toggle routine should handle multiple status toggles.
-dig, why mpd does not sends empty publish request on start (?)
+toggle routine should handle multiple status toggles
+think about how tune notification should act on start/quit
+timers deinitialization on module unloading
 clarify situation with disco, maybe it is due to this we are unable to receive pep events
-improve pubsub interface
 
--- a/examples/jobs.lua	Sun Mar 15 23:57:49 2009 +0200
+++ b/examples/jobs.lua	Mon Mar 16 00:47:44 2009 +0200
@@ -27,7 +27,7 @@
 
 main.command ( 'delay',
 	function ( args )
-		args = parse_args ( args )
+		args = main.parse_args ( args )
 		local who
 		if args.t then
 			who = args.t
@@ -42,7 +42,7 @@
 
 main.command ( 'job',
 	function ( args )
-		args = parse_args ( 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
--- a/examples/mcabberrc.lua	Sun Mar 15 23:57:49 2009 +0200
+++ b/examples/mcabberrc.lua	Mon Mar 16 00:47:44 2009 +0200
@@ -138,25 +138,6 @@
 	end
 end
 
-function parse_args ( args )
-	local ret        = {}
-	local still_opts = true
-	local option     = nil
-	local arglist    = main.parse_args ( args )
-	for k, word in pairs ( arglist ) do
-		if still_opts and not option and word:sub ( 1, 1 ) == '-' then
-			option      = word:sub ( 2 )
-		elseif option then
-			ret[option] = word
-			option      = nil
-		else
-			still_opts  = false
-			table.insert ( ret, word )
-		end
-	end
-	return ret
-end
-
 -- COMMANDS
 
 -- Help strings should not contain command, only arguments. This is necessary to support soft aliases.
--- a/examples/xep0004.lua	Sun Mar 15 23:57:49 2009 +0200
+++ b/examples/xep0004.lua	Mon Mar 16 00:47:44 2009 +0200
@@ -107,7 +107,7 @@
 
 main.command ( 'form',
 	function ( args )
-		args = parse_args ( args )
+		args = main.parse_args ( args )
 		local action = args[1]
 		local id = tonumber (args[1])
 		if forms[id] then
--- a/examples/xep0030.lua	Sun Mar 15 23:57:49 2009 +0200
+++ b/examples/xep0030.lua	Mon Mar 16 00:47:44 2009 +0200
@@ -55,7 +55,7 @@
 
 main.command ( 'disco',
 	function ( args )
-		args = parse_args ( args )
+		args = main.parse_args ( args )
 		local who
 		if args.t then
 			who = args.t
--- a/examples/xep0047.lua	Sun Mar 15 23:57:49 2009 +0200
+++ b/examples/xep0047.lua	Mon Mar 16 00:47:44 2009 +0200
@@ -167,7 +167,7 @@
 
 main.command ( 'ibb',
 	function ( args )
-		args = parse_args ( args )
+		args = main.parse_args ( args )
 		local action = args[1]
 		if action == 'send' then
 			local who
--- a/examples/xep0060.lua	Sun Mar 15 23:57:49 2009 +0200
+++ b/examples/xep0060.lua	Mon Mar 16 00:47:44 2009 +0200
@@ -306,7 +306,7 @@
 
 main.command ( 'node',
 	function ( args )
-		args = parse_args ( args )
+		args = main.parse_args ( args )
 		local who, action, node = args.t, args[1], args[2]
 		if not who then
 			who = main.current_buddy ()
--- a/examples/xep0146.lua	Sun Mar 15 23:57:49 2009 +0200
+++ b/examples/xep0146.lua	Mon Mar 16 00:47:44 2009 +0200
@@ -41,7 +41,7 @@
 
 main.command ( 'remote',
 	function ( args )
-		args = parse_args ( args )
+		args = main.parse_args ( args )
 		local who
 		if args.t then
 			who = args.t
--- a/examples/xep0163.lua	Sun Mar 15 23:57:49 2009 +0200
+++ b/examples/xep0163.lua	Mon Mar 16 00:47:44 2009 +0200
@@ -269,7 +269,7 @@
 	end, boolean_cid )
 main.command ( 'mood',
 	function ( args )
-		args = parse_args ( args )
+		args = main.parse_args ( args )
 		local data = { }
 		local mood, text = args[1], args[2]
 		if text then
@@ -282,7 +282,7 @@
 	end )
 main.command ( 'activity',
 	function ( args )
-		args = parse_args ( args )
+		args = main.parse_args ( args )
 		local data = { }
 		local activity, text = args[1], args[2]
 		if text then
@@ -302,7 +302,7 @@
 	end )
 main.command ( 'location',
 	function ( args )
-		args = parse_args ( args )
+		args = main.parse_args ( args )
 		local data = { }
 		for key, val in pairs ( args ) do
 			data[key] = { val }
--- a/main.c	Sun Mar 15 23:57:49 2009 +0200
+++ b/main.c	Mon Mar 16 00:47:44 2009 +0200
@@ -545,16 +545,20 @@
 }
 
 /// main.parse_args
-/// Function to parse command argument string. Returns table of strings.
-/// It can parse barewords (with escapes), double-quoted strings (with escapes) and single-quoted strings (without escapes).
-/// If args string ends with escape symbol, it is preserved.
+/// Function to parse command argument string.
+/// 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.
-/// Note: As for now, it does not preserve leading quote symbol for non-closed quoted strings. You should not rely on this behaviour.
-/// Examples:
+/// This strings are equal:
 /// * ab\ cd\'e\\f\"
 /// * "ab cd'e\\f\""
-/// * 'ab cd'\''e\f'
-/// * ab" cd'"'e\f'
+/// * 'ab cd'\''e\f"'
+/// * ab" cd'"'e\f"'
+/// Returned table have option names as keys, option values as values, and arguments as sequential members. -- option is supported.
+/// Example: "-t jid -m 9 -- -aa bb cc" will result in { t = 'jid', m = 9, '-aa', 'bb', 'cc' }
+/// Implementation notes:
+/// * 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)
@@ -562,6 +566,8 @@
 	const char  *args = luaL_checkstring (L, 1);
 	const char  *p    = args;
 	luaL_Buffer  buf;
+	int          option  = 0;
+	int          options = 1;
 
 	lua_newtable (L);
 	luaL_buffinit (L, &buf);
@@ -585,14 +591,14 @@
 				else
 					++p;
 			}
-			luaL_addlstring (&buf, start, p - start);
+			luaL_addlstring (&buf, start, p - start); // XXX: eats quote on eol
 			if (*p)
 				++p;
 		} else if (*p == '\'') { // no-escape quote
 			const char *start = ++p;
 			while (*p && *p != '\'')
 				p++;
-			luaL_addlstring (&buf, start, p - start);
+			luaL_addlstring (&buf, start, p - start); // XXX: eats quote on eol
 			if (*p)
 				++p;
 		} else { // bareword
@@ -614,11 +620,33 @@
 		}
 
 		if ((!*p) || *p == ' ') {
+			const char *result;
 			luaL_pushresult (&buf);
-			luaL_ref (L, -2);
+
+			result = lua_tostring (L, -1);
+			if (options && !option && *result == '-') { // option
+				if (*(result+1) == '-' && !*(result+2)) { // end of options
+					lua_pop (L, 1);
+					options = 0;
+				} else { // option name
+					lua_pushstring (L, result + 1);
+					lua_remove (L, -2);
+					option = 1;
+				}
+			} else if (option) { // opion value
+				lua_settable (L, -3);
+				option = 0;
+			} else { // argument
+				options = 0;
+				luaL_ref (L, -2);
+			}
+
 			luaL_buffinit (L, &buf);
 		}
 	}
+	if (option)
+		luaL_ref (L, -2); // XXX: eats minus on eol
+
 	return 1;
 }