--- a/TODO Sun Mar 15 15:03:51 2009 +0200
+++ b/TODO Sun Mar 15 20:45:24 2009 +0200
@@ -10,5 +10,7 @@
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
+dig, why mpd does not sends empty publish request on start (?)
+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 15:03:51 2009 +0200
+++ b/examples/jobs.lua Sun Mar 15 20:45:24 2009 +0200
@@ -31,28 +31,33 @@
local who
if args.t then
who = args.t
- args.t = nil
else
who = main.current_buddy ()
end
local stat = args[1]
- args[1] = nil
+ local mess = args[2]
delayed_jobs[who] = { }
- delayed_jobs[who][stat] = 'say_to -q ' .. who .. ' ' .. rebuild_args_string ( args )
+ delayed_jobs[who][stat] = 'say_to -q ' .. who .. ' ' .. mess
end )
main.command ( 'job',
function ( args )
- local action, jid, stat = args:match ( "(%w+)%s+(%w+)%s+(%w)" )
+ args = parse_args ( args )
+ local action, jid, stat = args[1], args[2], args[3]
if action == 'del' then
delayed_jobs[jid][stat] = nil
else
- print ( 'List of jobs:' )
+ local text = ''
for jid, jobs in pairs ( delayed_jobs ) do
for status, job in pairs ( jobs ) do
- print ( ' - ' .. jid .. ' -> ' .. status .. ': ' .. job )
+ text = text .. '\n - ' .. jid .. ' -> ' .. status .. ': ' .. job
end
end
+ if text ~= '' then
+ print ( 'List of jobs:' .. text )
+ else
+ print ( 'No jobs' )
+ end
end
end, { "del" } )
--- a/examples/mcabberrc.lua Sun Mar 15 15:03:51 2009 +0200
+++ b/examples/mcabberrc.lua Sun Mar 15 20:45:24 2009 +0200
@@ -72,8 +72,6 @@
reset bindings
- you can omit unregistering of commands and xmpp features - module will unregister
them automatically.
-- for now you can use parse_args and rebuild_args_string to deal with argument string,
- but this is likely to be replaced with something more perfect.
--]]
@@ -140,57 +138,25 @@
end
end
--- FIXME: eats spaces
function parse_args ( args )
- local ret = {}
+ local ret = {}
local still_opts = true
- local optname
- local option = false
- for word in args:gmatch ( "%S+" ) do
+ 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 = true
- optname = word:sub ( 2 )
+ option = word:sub ( 2 )
elseif option then
- ret[optname] = word
- option = false
+ ret[option] = word
+ option = nil
else
- still_opts = false
+ still_opts = false
table.insert ( ret, word )
end
end
return ret
end
--- FIXME: get rid of this
-function rebuild_args_string ( tab )
- local flags = nil
- local args = nil
- for k, v in pairs (tab) do
- if type (k) == 'number' then
- if args then
- args = args .. ' ' .. v
- else
- args = v
- end
- else
- if flags then
- flags = flags .. ' ' .. '-' .. k .. ' ' .. v
- else
- flags = '-' .. k .. ' ' .. v
- end
- end
- end
- if flags and args then
- return flags .. ' ' .. args
- elseif flags then
- return flags
- elseif args then
- return args
- else
- return ''
- end
-end
-
-- COMMANDS
-- Help strings should not contain command, only arguments. This is necessary to support soft aliases.
--- a/examples/xep0004.lua Sun Mar 15 15:03:51 2009 +0200
+++ b/examples/xep0004.lua Sun Mar 15 20:45:24 2009 +0200
@@ -1,6 +1,6 @@
forms = { }
-form_cid = 0
+form_cid = main.add_category { 'del', 'send' }
function insert_form ( form )
table.insert ( forms, form )
@@ -105,37 +105,35 @@
end
end
-form_cid = main.command ( 'form',
+main.command ( 'form',
function ( args )
args = parse_args ( args )
local action = args[1]
local id = tonumber (args[1])
if forms[id] then
- if args[2] then
- local field = args[2]
- if args[3] == 'clear' then
- form_set ( id, field, nil )
- else
- args[1] = nil
- args[2] = nil
- if args[3] == 'set' then
- args[3] = nil
- end
- form_set ( id, field, rebuild_args_string ( args ) )
+ local field = args[2]
+ if field then
+ local value = args[3]
+ if value == 'clear' then
+ value = nil
+ elseif value == 'set' then
+ value = args[4]
end
+ form_set ( id, field, value )
else
print ( 'Form: ' .. ( forms[id].title or '' ) .. '\n' .. forms[id].exp )
- print ( 'Fields:' )
+ local text = 'Fields:'
for index, field in ipairs ( forms[id].val ) do -- this should not be here, but setting up callback just for this...
if field.type == 'jid-multi' or field.type == 'list-multi' or field.type == 'text-multi' then
- print ( ' - ' .. field.var .. ' [' .. field.type .. ']:' )
+ text = text .. '\n - ' .. field.var .. ' [' .. field.type .. ']:'
for vin, value in ipairs ( field.value ) do
- print ( ' * ' .. value[1] )
+ text = text .. '\n * ' .. value[1]
end
else
- print ( ' - ' .. field.var .. ' [' .. field.type .. ']: ' .. field.value[1] )
+ text = text .. '\n - ' .. field.var .. ' [' .. field.type .. ']: ' .. field.value[1]
end
end
+ print ( text )
end
elseif action == 'del' then
forms[tonumber(args[2])] = nil
@@ -146,12 +144,17 @@
form.send ( form )
end
else
- print ( 'Forms list:' )
- for id, form in ipairs ( forms ) do
- print ( ' - ' .. tostring(id) .. ' ' .. form.title .. ' [' .. ( form.status or 'unknown' ) .. ']' )
+ local text = ''
+ for id, form in pairs ( forms ) do
+ text = text .. '\n - ' .. id .. ' ' .. form.title .. ' [' .. ( form.status or 'unknown' ) .. ']'
+ end
+ if text ~= '' then
+ print ( 'Forms list:' .. text )
+ else
+ print ( 'No forms' )
end
end
- end, { "del", "send" } )
+ end, 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 Sun Mar 15 15:03:51 2009 +0200
+++ b/examples/xep0030.lua Sun Mar 15 20:45:24 2009 +0200
@@ -59,16 +59,11 @@
local who
if args.t then
who = args.t
- args.t = nil
else
who = main.full_jid ()
end
if args[1] == 'items' then
- local node = nil
- if args[2] then
- args[1] = nil
- node = rebuild_args_string ( args )
- end
+ local node = args[2]
disco_items (
function ( items )
if type ( items ) == 'string' then
--- a/examples/xep0047.lua Sun Mar 15 15:03:51 2009 +0200
+++ b/examples/xep0047.lua Sun Mar 15 20:45:24 2009 +0200
@@ -168,35 +168,37 @@
main.command ( 'ibb',
function ( args )
args = parse_args ( args )
- if args[1] == 'send' then
+ local action = args[1]
+ if action == 'send' then
local who
if args.t then
who = args.t
- args.t = nil
else
who = main.full_jid ()
end
- args[1] = nil
- send_file ( who, rebuild_args_string ( args ) )
- elseif args[1] == 'accept' then
+ send_file ( who, args[2] )
+ elseif action == 'accept' then
local id = args[2]
- args[1] = nil
- args[2] = nil
if receiving_files[id] then
- receiving_files[id].accept ( rebuild_args_string ( args ) )
+ receiving_files[id].accept ( args[3] )
end
- elseif args[1] == 'reject' then
+ elseif action == 'reject' then
local id = args[2]
if receiving_files[id] then
receiving_files[id].reject ()
end
- elseif args[1] == 'del' then
+ elseif action == 'del' then
local id = args[2]
receiving_files[id] = nil
else
- print ( 'List of incoming streams:' )
+ local text = ''
for sid, data in pairs ( receiving_files ) do
- print ( sid .. ': ' .. ( data.name or '(not set)' ) .. ' [' .. data.status .. ']' )
+ text = text .. '\n' .. sid .. ': ' .. ( data.name or '(not set)' ) .. ' [' .. data.status .. ']'
+ end
+ if text ~= '' then
+ print ( 'List of incoming streams:' .. text )
+ else
+ print ( 'No streams' )
end
end
end, { "send", "accept", "reject", "del" } )
--- a/examples/xep0060.lua Sun Mar 15 15:03:51 2009 +0200
+++ b/examples/xep0060.lua Sun Mar 15 20:45:24 2009 +0200
@@ -165,27 +165,29 @@
main.command ( 'subscribe',
function ( args )
- pubsub_subscribe ( main.current_buddy (), args )
+ args = parse_args ( args )
+ pubsub_subscribe ( args.t or main.current_buddy (), args )
end )
main.command ( 'unsubscribe',
function ( args )
- pubsub_unsubscribe ( main.current_buddy (), args )
+ args = parse_args ( args )
+ pubsub_unsubscribe ( args.t or main.current_buddy (), args )
end )
main.command ( 'configure_node',
function ( args )
- pubsub_configure_node ( main.current_buddy (), args )
+ args = parse_args ( args )
+ pubsub_configure_node ( args.t or main.current_buddy (), args )
end )
main.command ( 'subscriptions',
function ( args )
- pubsub_list_subscriptions ( main.current_buddy (), args )
+ args = parse_args ( args )
+ pubsub_list_subscriptions ( args.t or main.current_buddy (), args )
end )
main.command ( 'subscription',
function ( args )
- local node, jid, state, id = args:match ( '(.-)%s+(.-)%s+(.-)%s+(.+)' )
- if not node then
- node, jid, state = args:match ( '(.-)%s+(.-)%s+(.+)' )
- end
- pubsub_modify_subscription ( main.current_buddy (), node, jid, state, id )
+ args = parse_args ( args )
+ local node, jid, state, id = args[1], args[2], args[3], args[4]
+ pubsub_modify_subscription ( args.t or main.current_buddy (), node, jid, state, id )
end )
commands_help['subscribe'] = "node_name\n\nSends pubsub subscription request to specified node of current buddy."
--- a/examples/xep0146.lua Sun Mar 15 15:03:51 2009 +0200
+++ b/examples/xep0146.lua Sun Mar 15 20:45:24 2009 +0200
@@ -44,27 +44,32 @@
local who
if args.t then
who = args.t
- args.t = nil
else
who = main.full_jid ()
end
- if args[1] == 'list' or not args[1] then
+ local action = args[1]
+ if not action then
disco_items (
function ( items )
if type ( items ) == 'string' then
main.print_info ( who, string.format ( "Service items discovery for %s (http://jabber.org/protocol/commands) failed: %s", who, items ) )
else
- main.print_info ( who, 'Available commands:' )
+ local text = ''
for index, item in ipairs ( items ) do
- main.print_info ( who, ' - ' .. item.node )
+ text = text .. '\n - ' .. item.node
+ end
+ if text ~= '' then
+ main.print_info ( who, 'Available commands:' .. text )
+ else
+ main.print_info ( who, 'No commands available.' )
end
end
end, who, 'http://jabber.org/protocol/commands' )
- elseif args[1] then
- remote_command ( who, args[1] )
+ elseif action then
+ remote_command ( who, action )
end
end, 'jid' )
-commands_help['remote'] = "[-t target_jid] [list | command]\n\nPrints list of available remote command or requests execution of specified command."
+commands_help['remote'] = "[-t target_jid] [remote_command]\n\nPrints list of available remote command or requests execution of specified command."
-- vim: se ts=4: --
--- a/examples/xep0163.lua Sun Mar 15 15:03:51 2009 +0200
+++ b/examples/xep0163.lua Sun Mar 15 20:45:24 2009 +0200
@@ -269,12 +269,11 @@
end, boolean_cid )
main.command ( 'mood',
function ( args )
+ args = parse_args ( args )
local data = { }
- local mood, text = args:match ( "(%S-)%s+(.+)" )
- if mood then
+ local mood, text = args[1], args[2]
+ if text then
data.text = { text }
- else
- mood = args
end
if mood ~= '' then
data[mood] = { }
@@ -283,14 +282,13 @@
end )
main.command ( 'activity',
function ( args )
+ args = parse_args ( args )
local data = { }
- local activity, text = args:match ( "(%S-)%s+(.+)" )
- if activity then
+ local activity, text = args[1], args[2]
+ if text then
data.text = { text }
- else
- activity = args
end
- local act, subact = activity:match ( ".-%-.+" )
+ local act, subact = activity:match ( "(.-)%-(.+)" )
if not act then
act = activity
end
@@ -304,11 +302,10 @@
end )
main.command ( 'location',
function ( args )
+ args = parse_args ( args )
local data = { }
- local key, val, remains = args:match ( '%s*(%S+)%s+(%S+)(.*)' )
- while key do
+ for key, val in pairs ( args ) do
data[key] = { val }
- key, val, remains = args:match ( '%s*(%S+)%s+(%S+)(.*)' )
end
pep_publish ( 'geoloc', data )
end )
@@ -316,7 +313,7 @@
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."
commands_help['activity'] = "[activity[-specific_activity] [text]]\n\nPublishes your activity.\nNote, that for now it does not checks for activity validity, so, see xep0108 for valid activity values."
-commands_help['location'] = "[key value [key value ...]]\n\nPublishes your current geolocation.\nValid keys are accuracy, alt, area, bearing, building, country, datum, description, error, floor, lat, locality, lon, postalcode, region, room, speed, street, text, timestamp and uri, according to xep0080."
+commands_help['location'] = "[-key value [-key value ...]]\n\nPublishes your current geolocation.\nValid keys are accuracy, alt, area, bearing, building, country, datum, description, error, floor, lat, locality, lon, postalcode, region, room, speed, street, text, timestamp and uri, according to xep0080."
pep_handler_registered = false
--- a/main.c Sun Mar 15 15:03:51 2009 +0200
+++ b/main.c Sun Mar 15 20:45:24 2009 +0200
@@ -534,7 +534,7 @@
/// command function
/// Function to handle newly registered command.
/// A: string (arguments)
-void lua_main_command_handler (char *args, lua_command_callback_t *cb)
+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);
@@ -544,6 +544,84 @@
}
}
+/// 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.
+/// 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:
+/// * ab\ cd\'e\\f\"
+/// * "ab cd'e\\f\""
+/// * 'ab cd'\''e\f'
+/// * ab" cd'"'e\f'
+/// A: string
+/// R: table
+static int lua_main_parse_args (lua_State *L)
+{
+ const char *args = luaL_checkstring (L, 1);
+ const char *p = args;
+ luaL_Buffer buf;
+
+ lua_newtable (L);
+ luaL_buffinit (L, &buf);
+ while (*p) {
+ if (*p == ' ') {
+ ++p;
+ continue;
+ }
+ if (*p == '"') { // soft quote
+ const char *start = ++p;
+ while (*p) {
+ if (*p == '\\') { // escape symbol
+ luaL_addlstring (&buf, start, p - start);
+ start = ++p;
+ if (*p) // skip symbol
+ ++p;
+ else // add last \ in line
+ luaL_addchar (&buf, '\\');
+ } else if (*p == '"') // quotation end
+ break;
+ else
+ ++p;
+ }
+ luaL_addlstring (&buf, start, p - start);
+ if (*p)
+ ++p;
+ } else if (*p == '\'') { // no-escape quote
+ const char *start = ++p;
+ while (*p && *p != '\'')
+ p++;
+ luaL_addlstring (&buf, start, p - start);
+ if (*p)
+ ++p;
+ } else { // bareword
+ const char *start = p;
+ while (*p) {
+ if (*p == '\\') {
+ luaL_addlstring (&buf, start, p - start);
+ start = ++p;
+ if (*p) // skip symbol
+ ++p;
+ else // add last \ in line
+ luaL_addchar (&buf, '\\');
+ } else if (*p == ' ' || *p == '\'' || *p == '"')
+ break;
+ else
+ ++p;
+ }
+ luaL_addlstring (&buf, start, p - start);
+ }
+
+ if ((!*p) || *p == ' ') {
+ luaL_pushresult (&buf);
+ luaL_ref (L, -2);
+ luaL_buffinit (L, &buf);
+ }
+ }
+ return 1;
+}
+
/// main.add_category
/// Adds completion category.
/// A: table (values are used as words for completion, optional)
@@ -894,6 +972,7 @@
reg ( binding )
reg ( add_feature )
reg ( del_feature )
+ reg ( parse_args )
reg ( add_category )
reg ( del_category )
reg ( add_completion )