summaryrefslogtreecommitdiffstats
path: root/apps/plugins/lua_scripts/print_lua_func.lua
blob: f2642a187bab9f1a64afcff3bb7b5f61d9c57271 (plain)
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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
--RB LUA show all global variables; BILGUS
require "actions"
require "audio"
require "buttons"
require "color"
require "draw"
require "image"
require "lcd"
require "math_ex"
require "pcm"
require "playlist"
require "print"
--require "settings" --uses a lot of memory
require "sound"
collectgarbage("collect")

local sDumpFile = "/rb-lua_functions.txt"
local filehandle

local function a2m_m2a(addr_member)
    --turns members into addresses; addresses back into members
    return addr_member
end

local function dtTag(sType)
--convert named type; 'number'.. to short type '[n]...'
--if '?' supplied print out datatype key; number = [n]...
    local retType = "?"
    local typ = {
                ["nil"] = "nil",
                ["boolean"]  = "b",
                ["number"] = "n",
                ["string"] = "s",
                ["userdata"] = "u",
                ["function"] = "f",
                ["thread"] = "thr",
                ["table"] = "t"
                }
    if sType == "?" then retType = "Datatypes: " end
    for k,v in pairs(typ) do
        if sType == k then
            retType = v break
        elseif (sType == "?") then
            retType = retType .. "  [" ..v.. "] = " .. k
        end
    end
    return " [" ..retType.. "] "
end

local function tableByName(tName)
    --find the longest match possible to an actual table
    --Name comes in as (table) tName.var so we can pass back out the name found PITA
    --returns the table found (key and value)
    local ld = {}
    local sMatch = ""
    local kMatch = nil
    local vMatch = nil

----FUNCTIONS for tableByName -----------------------------------------------------
    local function search4Str(n, k, v)
        local sKey = tostring(k)
        if string.find (n, sKey,1,true) then
            if sKey:len() > sMatch:len() then sMatch = sKey kMatch = k  vMatch = v end
            --find the longest match we can
        end
    end
----END FUNCTIONS for tableByName -------------------------------------------------

    if tName.val ~= nil and tName.val ~= "" then
        for k, v in pairs(_G) do
        --_G check both since some tables are only in _G or package.loaded
            search4Str(tName.val, k, v)
        end
        for k, v in pairs(package.loaded) do --package.loaded
            search4Str(tName.val, k, v)
        end
        if not string.find (sMatch, "_G",1,true) then sMatch = "_G." .. sMatch end
        -- put the root _G in if not exist
        if kMatch and vMatch then ld[kMatch] = vMatch tName.val = sMatch return ld end
    end
    tName.val = "_G"
    return package.loaded --Not Found return default
end

local function dump_Tables(tBase, sFunc, tSeen, tRet)
    --Based on: http://www.lua.org/cgi-bin/demo?globals
    --Recurse through tBase tables copying all found Tables
    local sSep=""
    local ld={}
    local tNameBuf = {}
    local sName
    if sFunc ~= "" then sSep = "." end

    for k, v in pairs(tBase) do
        k = tostring(k)
        tNameBuf[1] = sFunc
        tNameBuf[2] = sSep
        tNameBuf[3] = k


        if k ~= "loaded" and type(v) == "table" and not tSeen[v] then
            tSeen[v]=sFunc
            sName = table.concat(tNameBuf)
            tRet[sName] = a2m_m2a(v) --place all keys into ld[i]=value
            dump_Tables(v, sName, tSeen, tRet)
        elseif type(v) == "table" and not tSeen[v] then
            tSeen[v]=sFunc
            tRet[table.concat(tNameBuf)] = a2m_m2a(v) -- dump 'loaded' table
            for k1, v1 in pairs(v) do
                if not _G[k1] and type(v1) == "table" and not tSeen[v1] then
                    -- dump tables that are loaded but not global
                    tSeen[v1]=sFunc
                    tNameBuf[3] = k1
                    sName = table.concat(tNameBuf)
                    tRet[sName] = a2m_m2a(v1) --place all keys into ld[i]=value
                    dump_Tables(v1, sName, tSeen, tRet)
                end
            end
        end
    end
end

local function dump_Functions(tBase)
    --Based on: http://www.lua.org/cgi-bin/demo?globals
    --We already recursed through tBase copying all found tables
    --we look up the table by name and then (ab)use a2m_m2a() to load the address
    --after finding the table by address in tBase we will
        --put the table address of tFuncs in its place
    local tFuncBuf = {}
    for k,v in pairs(tBase) do
        local tTable = a2m_m2a(v)
        local tFuncs = {}

        for key, val in pairs(tTable) do
            if key ~= "loaded" then
                tFuncBuf[1] = dtTag(type(val))
                tFuncBuf[2] = tostring(key)
                tFuncs[table.concat(tFuncBuf)]= val
                --put the name and value in our tFuncs table
            end
        end
        tBase[k] = a2m_m2a(tFuncs) -- copy the address back to tBase
    end

end

local function get_common_branches(t, tRet)
    --load t 'names(values)' into keys
    --strip off long paths then iterate value if it exists
    --local tRet={}
    local sBranch = ""
    local tName = {}
    for k in pairs(t) do
            tName["val"]=k
            tableByName(tName)
            sBranch = tName.val
            if tRet[sBranch] == nil then
                tRet[sBranch] = 1 --first instance of this branch
            else
                tRet[sBranch] = tRet[sBranch] + 1
            end
    end
end

local function pairsByPairs (t, tkSorted)
    --tkSorted should be an already sorted (i)table with t[keys] in the values
    --https://www.lua.org/pil/19.3.html
    --!!Note: table sort default function does not like numbers as [KEY]!!
    --see *sortbyKeys*cmp_alphanum*

    local i = 0      -- iterator variable
    local iter = function ()   -- iterator function
        i = i + 1
        if tkSorted[i] == nil then return nil
            else return tkSorted[i], t[tkSorted[i]]
        end
    end
    return iter
end

local function sortbyKeys(t, tkSorted)
    --loads keys of (t) into values of tkSorted
    --and then sorts them
    --tkSorted has integer keys (see ipairs)
----FUNCTIONS for sortByKeys -------------
    local cmp_alphanum = function (op1, op2)
                            local type1= type(op1)
                            local type2 = type(op2)
                            if type1 ~= type2 then
                                return type1 < type2
                            else
                                return op1 < op2
                            end
                        end
----END FUNCTIONS for sortByKeys ---------
    for n in pairs(t) do table.insert(tkSorted, n) end
    table.sort(tkSorted, cmp_alphanum)--table.sort(tkSorted)
end

local function funcprint(tBuf, strName, value)
        local sType = type(value)
        local sVal = ""
        local sHex = ""
    tBuf[#tBuf + 1] = "\t"
    tBuf[#tBuf + 1] = strName
    if nil ~= string.find (";string;number;userdata;boolean;", sType, 1, true) then
        --If any of the above types print the contents of variable
        sVal = tostring(value)

        if type(value) == "number" then
            sHex = " = 0x" .. string.format("%x", value)
        else
            sHex = ""
            sVal = string.gsub(sVal, "\n", "\\n") --replace newline with \n
        end
        tBuf[#tBuf + 1] = " : "
        tBuf[#tBuf + 1] = sVal
        tBuf[#tBuf + 1] = sHex
    end
    tBuf[#tBuf + 1] = "\r\n"
end

local function errorHandler( err )
    filehandle:write(" ERROR:" .. err .. "\n")
end


------------MAIN----------------------------------------------------------------
    local _NIL = nil
    local tSeen= {}
    local tcBase = {}
    local tkSortCbase = {}
    local tMods= {}
    local tkSortMods = {}
    local tWriteBuf = {}
    local n = 0 -- count of how many items were found

    filehandle = io.open(sDumpFile, "w+") --overwrite
    tWriteBuf[#tWriteBuf + 1] = "*Loaded Modules* \n"

    xpcall( function()
                dump_Tables(tableByName({["val"] = "_G"}),"", tSeen, tMods)
                --you can put a table name here if you just wanted to display
                --only its items, ex. "os" or "rb" or "io"
                --However, it has to be accessible directly from _G
                --so "rb.actions" wouldn't return anything since its technically
                --enumerated through _G.rb
            end , errorHandler )
    tSeen = nil

    xpcall( function()dump_Functions(tMods)end , errorHandler )

    get_common_branches(tMods, tcBase)

    sortbyKeys(tcBase, tkSortCbase)
    sortbyKeys(tMods, tkSortMods)

    for k, v in pairsByPairs(tcBase, tkSortCbase ) do
        n = n + 1
        if n ~= 1 then
            tWriteBuf[#tWriteBuf + 1] = ", "
        end
        tWriteBuf[#tWriteBuf + 1] = tostring(k)
        if n >= 3 then -- split loaded modules to multiple lines
            n = 0
            tWriteBuf[#tWriteBuf + 1] = "\r\n"
        end
        if #tWriteBuf > 25 then
            filehandle:write(table.concat(tWriteBuf))
            for i=1, #tWriteBuf do tWriteBuf[i] = _NIL end -- reuse table
        end
    end

    tcBase= nil tkSortCbase= nil
    tWriteBuf[#tWriteBuf + 1] = "\r\n"
    tWriteBuf[#tWriteBuf + 1] = dtTag("?")
    tWriteBuf[#tWriteBuf + 1] = "\r\n\r\n"
    tWriteBuf[#tWriteBuf + 1] = "Functions: \r\n"

    n = 0
    for key, val in pairsByPairs(tMods, tkSortMods) do
        local tkSorted = {}
        local tFuncs = a2m_m2a(val)
        sortbyKeys(tFuncs, tkSorted)
        tWriteBuf[#tWriteBuf + 1] = "\r\n"
        tWriteBuf[#tWriteBuf + 1] = tostring(key)
        tWriteBuf[#tWriteBuf + 1] = "\r\n"
        for k, v in pairsByPairs(tFuncs, tkSorted) do
            n = n + 1
            funcprint(tWriteBuf, k,v)
            if #tWriteBuf > 25 then
                filehandle:write(table.concat(tWriteBuf))
                for i=1, #tWriteBuf do tWriteBuf[i] = _NIL end -- reuse table
            end
        end
    end
    tWriteBuf[#tWriteBuf + 1] = "\r\n\r\n"
    tWriteBuf[#tWriteBuf + 1] = n
    tWriteBuf[#tWriteBuf + 1] = " Items Found \r\n"
    filehandle:write(table.concat(tWriteBuf))
    for i=1, #tWriteBuf do tWriteBuf[i] = _NIL end -- empty table
    filehandle:close()
    rb.splash(rb.HZ * 5, n .. " Items dumped to : " .. sDumpFile)
    --rb.splash(500, collectgarbage("count"))