aboutsummaryrefslogtreecommitdiffstats
path: root/src/irc/misc.lua
blob: 7f77eeae813813381f14ea2bc50aeeb9450b9f85 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
-- initialization {{{
local base =      _G
local irc_debug = require 'irc.debug'
local socket =    require 'socket'
local math =      require 'math'
local os =        require 'os'
local string =    require 'string'
local table =     require 'table'
-- }}}

module 'irc.misc'

-- defaults {{{
DELIM = ' '
PATH_SEP = '/'
ENDIANNESS = "big"
INT_BYTES = 4
-- }}}

-- private functions {{{
local function exists(filename)
    local _, err = os.rename(filename, filename)
    if not err then return true end
    return not err:find("No such file or directory")
end
-- }}}

-- public functions {{{
-- split() - splits str into substrings based on several options {{{
function split(str, delim, end_delim, lquotes, rquotes)
    -- handle arguments {{{
    delim = "["..(delim or DELIM).."]"
    if end_delim then end_delim = "["..end_delim.."]" end
    if lquotes then lquotes = "["..lquotes.."]" end
    if rquotes then rquotes = "["..rquotes.."]" end
    local optdelim = delim .. "?"
    -- }}}

    local ret = {}
    local instring = false
    while str:len() > 0 do
        -- handle case for not currently in a string {{{
        if not instring then
            local end_delim_ind, lquote_ind, delim_ind
            if end_delim then end_delim_ind = str:find(optdelim..end_delim) end
            if lquotes then lquote_ind = str:find(optdelim..lquotes) end
            local delim_ind = str:find(delim)
            if not end_delim_ind then end_delim_ind = str:len() + 1 end
            if not lquote_ind then lquote_ind = str:len() + 1 end
            if not delim_ind then delim_ind = str:len() + 1 end
            local next_ind = math.min(end_delim_ind, lquote_ind, delim_ind)
            if next_ind == str:len() + 1 then
                table.insert(ret, str)
                break
            elseif next_ind == end_delim_ind then
                -- TODO: hackish here
                if str:sub(next_ind, next_ind) == end_delim:gsub('[%[%]]', '') then
                    table.insert(ret, str:sub(next_ind + 1))
                else
                    table.insert(ret, str:sub(1, next_ind - 1))
                    table.insert(ret, str:sub(next_ind + 2))
                end
                break
            elseif next_ind == lquote_ind then
                table.insert(ret, str:sub(1, next_ind - 1))
                str = str:sub(next_ind + 2)
                instring = true
            else -- last because the top two contain it
                table.insert(ret, str:sub(1, next_ind - 1))
                str = str:sub(next_ind + 1)
            end
        -- }}}
        -- handle case for currently in a string {{{
        else
            local endstr = str:find(rquotes..optdelim)
            table.insert(ret, str:sub(1, endstr - 1))
            str = str:sub(endstr + 2)
            instring = false
        end
        -- }}}
    end
    return ret
end
-- }}}

-- basename() - returns the basename of a file {{{
function basename(path, sep)
    sep = sep or PATH_SEP
    if not path:find(sep) then return path end
    return socket.skip(2, path:find(".*" .. sep .. "(.*)"))
end
-- }}}

-- dirname() - returns the dirname of a file {{{
function dirname(path, sep)
    sep = sep or PATH_SEP
    if not path:find(sep) then return "." end
    return socket.skip(2, path:find("(.*)" .. sep .. ".*"))
end
-- }}}

-- str_to_int() - converts a number to a low-level int {{{
function str_to_int(str, bytes, endian)
    bytes = bytes or INT_BYTES
    endian = endian or ENDIANNESS
    local ret = ""
    for i = 0, bytes - 1 do 
        local new_byte = string.char(math.fmod(str / (2^(8 * i)), 256))
        if endian == "big" or endian == "network" then ret = new_byte .. ret
        else ret = ret .. new_byte
        end
    end
    return ret
end
-- }}}

-- int_to_str() - converts a low-level int to a number {{{
function int_to_str(int, endian)
    endian = endian or ENDIANNESS
    local ret = 0
    for i = 1, int:len() do
        if endian == "big" or endian == "network" then ind = int:len() - i + 1
        else ind = i
        end
        ret = ret + string.byte(int:sub(ind, ind)) * 2^(8 * (i - 1))
    end
    return ret
end
-- }}}

-- ip_str_to_int() - converts a string ip address to an int {{{
function ip_str_to_int(ip_str)
    local i = 3
    local ret = 0
    for num in ip_str:gmatch("%d+") do
        ret = ret + num * 2^(i * 8)                  
        i = i - 1
    end
    return ret
end
-- }}}

-- ip_int_to_str() - converts an int to a string ip address {{{
function ip_int_to_str(ip_int)
    local ip = {}
    for i = 3, 0, -1 do
        local new_num = math.floor(ip_int / 2^(i * 8))
        table.insert(ip, new_num)
        ip_int = ip_int - new_num * 2^(i * 8)
    end 
    return table.concat(ip, ".")
end
-- }}}

-- get_unique_filename() - returns a unique filename {{{
function get_unique_filename(filename)
    if not exists(filename) then return filename end

    local count = 1
    while true do
        if not exists(filename .. "." .. count) then
            return filename .. "." .. count
        end
        count = count + 1
    end
end
-- }}}

-- try_call() - call a function, if it exists {{{
function try_call(fn, ...)
    if base.type(fn) == "function" then
        return fn(...)
    end
end
-- }}}

-- try_call_warn() - same as try_call, but complain if not {{{
function try_call_warn(msg, fn, ...)
    if base.type(fn) == "function" then
        return fn(...)
    else
        irc_debug.warn(msg)
    end
end
-- }}}

-- parse_user() - gets the various parts of a full username {{{
-- args: user - usermask (i.e. returned in the from field of a callback)
-- return: nick, username, hostname (these can be nil if nonexistant)
function parse_user(user)
    local found, bang, nick = user:find("^([^!]*)!")
    if found then 
        user = user:sub(bang + 1)
    else
        return user
    end
    local found, equals = user:find("^.=")
    if found then
        user = user:sub(3)
    end
    local found, at, username = user:find("^([^@]*)@")
    if found then
        return nick, username, user:sub(at + 1)
    else
        return nick, user
    end
end
-- }}}

-- value_iter() - iterate just over values of a table {{{
function value_iter(state, arg, pred)
    for k, v in base.pairs(state) do
        if arg == v then arg = k end
    end
    local key, val = base.next(state, arg)
    if not key then return end

    if base.type(pred) == "function" then
        while not pred(val) do
            key, val = base.next(state, key)
            if not key then return end
        end
    end
    return val
end
-- }}}
-- }}}