File

clix.lua @ 33:f1901c9de891

clix.lua: Improvements to argument handling and command detection. In particular options may now precede commands.
author Matthew Wild <mwild1@gmail.com>
date Sat, 29 May 2010 21:13:27 +0100
parent 32:cda6a004ff79
child 34:d4f8fc71d936
line wrap: on
line source

#!/usr/bin/env lua
-- Clix -- Command-line XMPP
-- Copyright (C) 2008-2010 Matthew Wild
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
require "verse"
require "verse.client"

-- Global to allow commands to add to it
short_opts = { v = "verbose", q = "quiet", t = "to", f = "from", e = "type",
	a = "account", p = "password", r = "resource", o = "presence", c = "chatroom" }

if #arg < 1 then
	print("Command Line XMPP, available commands:");
	for module in pairs(package.preload) do
		if module:match("^clix%.") then
			local m = require(module);
			io.write("\t", module:gsub("^clix%.", ""), ": ");
			m({ short_help = true }, {});
		end
	end
	return 0;
end

local accounts = { default = {} };
local current_account;

local config_path = (os.getenv("XDG_CONFIG_HOME") or (os.getenv("HOME").."/.config"));
local config_file, err = io.open(config_path.."/.clixrc") or io.open(config_path.."/.clix");

if not config_file then
	print("Unable to open config file... looked for "..config_path.."/.clixrc");
	if err then print(err); end
	os.exit(1);
end

for line in config_file:lines() do
	line = line:match("^%s*(.-)%s*$");
	if line:match("^%[") then
		current_account = line:match("^%[(.-)%]");
		accounts[current_account] = {};
		if not current_account then -- This is the first defined account
			accounts.default = accounts[current_account];
		end
	elseif current_account then
		local k,v = line:match("^(%w+)%s*[:=]%s*(.+)$");
		accounts[current_account or "default"][k] = v;
	end
end

function clix_connect(opts, on_connect)
	local account = accounts[opts.account or "default"];
	if not (account and account.jid) then
		io.stderr:write("The specified account (", opts.account or "default", ") wasn't found in the config file\n");
		return nil;
	end
	verse.set_logger(opts.verbose and print or function () end);
	local conn = verse.new(verse.logger());
	conn.log.debug = opts.verbose;
	conn:hook("authentication-failure", function (err)
		conn:error("Authentication failure ("..(err.condition or "unknown error")..")"..(err.text and (": "..err.text) or ""));
		conn:close();
	end);
	conn:hook("binding-success", function ()
		if not opts.quiet then
			io.stderr:write("clix: connected as ", conn.jid, "\n");
		end
		if opts.chatroom then
			conn:send(verse.presence{to=opts.to.."/"..(opts.nick or "clix")});
		end
		if opts.presence then
			conn:send(verse.presence());
		end
		return on_connect(conn);
	end);
	conn:hook("binding-failure", function (err)
		conn:error("Resource binding failure ("..(err.condition or "unknown error")..")"..(err.text and (": "..err.text) or ""));
		conn:close();
	end);
	conn:hook("disconnected", function (info)
		if info.reason then
			conn:warn("Disconnecting: %s", tostring(info.reason));
		end
		verse.quit();
	end);
	-- Optional config parameters
	conn.connect_host = account.address;
	conn.connect_port = account.port;
	
	-- Connect!
	conn:connect_client(account.jid, opts.password or account.password);
	
	if type(opts.resource) == "string" then
		conn.resource = opts.resource;
	end
	
	local ok, ret = pcall(verse.loop);
	if not ok and not ret:match("interrupted!$") then
		io.stderr:write("Fatal error: ", ret, "\n");
		return 1;
	end
	return err or 0;
end

table.remove(arg,1);

local opts = {};

local args_handled_up_to;
for i, opt in ipairs(arg) do
        if opt:match("^%-") and opt ~= "--" then
                local name = opt:match("^%-%-?([^%s=]+)()")
                name = (short_opts[name] or name):gsub("%-+", "_");
                if name:match("^no_") then
                        name = name:sub(4, -1);
                        opts[name] = false;
                else
                        opts[name] = opt:match("=(.*)$") or true;
                end
        else
        	args_handled_up_to = i-1;
		break;
        end
end

-- Remove all the handled args from the arg array
for n=(args_handled_up_to or #arg),1,-1 do
	table.remove(arg, n);
end

local command = arg[1];

local ok, m = pcall(require, "clix."..command);
if not ok then
	local is_debug;
	for i=1,#arg do
		if arg[i] == "--debug" then
			is_debug = true; break;
		end
	end
	print("Error running command '"..command..(is_debug and "" or "' (run with --debug to see full error)"));
	if is_debug then
		print(m);
	end
	return 1;
end

if type(m) ~= "function" then
	print(command.." is not a valid command");
	return 1;
end

return m(opts, arg) or 0;