3 local irc_debug = require 'irc.debug'
4 local socket = require 'socket'
5 local math = require 'math'
6 local os = require 'os'
7 local string = require 'string'
8 local table = require 'table'
20 -- private functions {{{
21 local function exists(filename)
22 local _, err = os.rename(filename, filename)
23 if not err then return true end
24 return not err:find("No such file or directory")
28 -- public functions {{{
29 -- split() - splits str into substrings based on several options {{{
30 function split(str, delim, end_delim, lquotes, rquotes)
31 -- handle arguments {{{
32 delim = "["..(delim or DELIM).."]"
33 if end_delim then end_delim = "["..end_delim.."]" end
34 if lquotes then lquotes = "["..lquotes.."]" end
35 if rquotes then rquotes = "["..rquotes.."]" end
36 local optdelim = delim .. "?"
40 local instring = false
41 while str:len() > 0 do
42 -- handle case for not currently in a string {{{
44 local end_delim_ind, lquote_ind, delim_ind
45 if end_delim then end_delim_ind = str:find(optdelim..end_delim) end
46 if lquotes then lquote_ind = str:find(optdelim..lquotes) end
47 local delim_ind = str:find(delim)
48 if not end_delim_ind then end_delim_ind = str:len() + 1 end
49 if not lquote_ind then lquote_ind = str:len() + 1 end
50 if not delim_ind then delim_ind = str:len() + 1 end
51 local next_ind = math.min(end_delim_ind, lquote_ind, delim_ind)
52 if next_ind == str:len() + 1 then
53 table.insert(ret, str)
55 elseif next_ind == end_delim_ind then
57 if str:sub(next_ind, next_ind) == end_delim:gsub('[%[%]]', '') then
58 table.insert(ret, str:sub(next_ind + 1))
60 table.insert(ret, str:sub(1, next_ind - 1))
61 table.insert(ret, str:sub(next_ind + 2))
64 elseif next_ind == lquote_ind then
65 table.insert(ret, str:sub(1, next_ind - 1))
66 str = str:sub(next_ind + 2)
68 else -- last because the top two contain it
69 table.insert(ret, str:sub(1, next_ind - 1))
70 str = str:sub(next_ind + 1)
73 -- handle case for currently in a string {{{
75 local endstr = str:find(rquotes..optdelim)
76 table.insert(ret, str:sub(1, endstr - 1))
77 str = str:sub(endstr + 2)
86 -- basename() - returns the basename of a file {{{
87 function basename(path, sep)
89 if not path:find(sep) then return path end
90 return socket.skip(2, path:find(".*" .. sep .. "(.*)"))
94 -- dirname() - returns the dirname of a file {{{
95 function dirname(path, sep)
97 if not path:find(sep) then return "." end
98 return socket.skip(2, path:find("(.*)" .. sep .. ".*"))
102 -- str_to_int() - converts a number to a low-level int {{{
103 function str_to_int(str, bytes, endian)
104 bytes = bytes or INT_BYTES
105 endian = endian or ENDIANNESS
107 for i = 0, bytes - 1 do
108 local new_byte = string.char(math.fmod(str / (2^(8 * i)), 256))
109 if endian == "big" or endian == "network" then ret = new_byte .. ret
110 else ret = ret .. new_byte
117 -- int_to_str() - converts a low-level int to a number {{{
118 function int_to_str(int, endian)
119 endian = endian or ENDIANNESS
121 for i = 1, int:len() do
122 if endian == "big" or endian == "network" then ind = int:len() - i + 1
125 ret = ret + string.byte(int:sub(ind, ind)) * 2^(8 * (i - 1))
131 -- ip_str_to_int() - converts a string ip address to an int {{{
132 function ip_str_to_int(ip_str)
135 for num in ip_str:gmatch("%d+") do
136 ret = ret + num * 2^(i * 8)
143 -- ip_int_to_str() - converts an int to a string ip address {{{
144 function ip_int_to_str(ip_int)
147 local new_num = math.floor(ip_int / 2^(i * 8))
148 table.insert(ip, new_num)
149 ip_int = ip_int - new_num * 2^(i * 8)
151 return table.concat(ip, ".")
155 -- get_unique_filename() - returns a unique filename {{{
156 function get_unique_filename(filename)
157 if not exists(filename) then return filename end
161 if not exists(filename .. "." .. count) then
162 return filename .. "." .. count
169 -- try_call() - call a function, if it exists {{{
170 function try_call(fn, ...)
171 if base.type(fn) == "function" then
177 -- try_call_warn() - same as try_call, but complain if not {{{
178 function try_call_warn(msg, fn, ...)
179 if base.type(fn) == "function" then
187 -- parse_user() - gets the various parts of a full username {{{
188 -- args: user - usermask (i.e. returned in the from field of a callback)
189 -- return: nick, username, hostname (these can be nil if nonexistant)
190 function parse_user(user)
191 local found, bang, nick = user:find("^([^!]*)!")
193 user = user:sub(bang + 1)
197 local found, equals = user:find("^.=")
201 local found, at, username = user:find("^([^@]*)@")
203 return nick, username, user:sub(at + 1)
210 -- value_iter() - iterate just over values of a table {{{
211 function value_iter(state, arg, pred)
212 for k, v in base.pairs(state) do
213 if arg == v then arg = k end
215 local key, val = base.next(state, arg)
216 if not key then return end
218 if base.type(pred) == "function" then
219 while not pred(val) do
220 key, val = base.next(state, key)
221 if not key then return end