# HG changeset patch # User Myhailo Danylenko # Date 1237142724 -7200 # Node ID 25552b21d3fbf24f0b0ec737132806ba3ae65449 # Parent e441162b13862aedc4b726c85917eb4a2f4ed435 Arguments parsing diff -r e441162b1386 -r 25552b21d3fb TODO --- 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 diff -r e441162b1386 -r 25552b21d3fb examples/jobs.lua --- 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" } ) diff -r e441162b1386 -r 25552b21d3fb examples/mcabberrc.lua --- 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. diff -r e441162b1386 -r 25552b21d3fb examples/xep0004.lua --- 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." diff -r e441162b1386 -r 25552b21d3fb examples/xep0030.lua --- 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 diff -r e441162b1386 -r 25552b21d3fb examples/xep0047.lua --- 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" } ) diff -r e441162b1386 -r 25552b21d3fb examples/xep0060.lua --- 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." diff -r e441162b1386 -r 25552b21d3fb examples/xep0146.lua --- 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: -- diff -r e441162b1386 -r 25552b21d3fb examples/xep0163.lua --- 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 diff -r e441162b1386 -r 25552b21d3fb main.c --- 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 )