summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/trac-fil.lua
blob: 8cc903e2a62c42dda9e99b2dba029fd2a9cf72ff (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
if not modules then modules = { } end modules ['trac-fil'] = {
    version   = 1.001,
    comment   = "for the moment for myself",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

local rawset, tonumber, type, pcall = rawset, tonumber, type, pcall
local format, concat = string.format, table.concat
local openfile = io.open
local date = os.date
local sortedpairs = table.sortedpairs

local P, C, Cc, Cg, Cf, Ct, Cs, Carg = lpeg.P, lpeg.C, lpeg.Cc, lpeg.Cg, lpeg.Cf, lpeg.Ct, lpeg.Cs, lpeg.Carg
local lpegmatch = lpeg.match

local patterns   = lpeg.patterns
local cardinal   = patterns.cardinal
local whitespace = patterns.whitespace^0

local timestamp = Cf(Ct("") * (
      Cg (Cc("year")    * (cardinal/tonumber)) * P("-")
    * Cg (Cc("month")   * (cardinal/tonumber)) * P("-")
    * Cg (Cc("day")     * (cardinal/tonumber)) * P(" ")
    * Cg (Cc("hour")    * (cardinal/tonumber)) * P(":")
    * Cg (Cc("minute")  * (cardinal/tonumber)) * P(":")
    * Cg (Cc("second")  * (cardinal/tonumber)) * P("+")
    * Cg (Cc("thour")   * (cardinal/tonumber)) * P(":")
    * Cg (Cc("tminute") * (cardinal/tonumber))
)^0, rawset)

local keysvalues = Cf(Ct("") * (
    Cg(C(patterns.letter^0) * whitespace * "=" * whitespace * Cs(patterns.unquoted) * whitespace)
)^0, rawset)

local statusline = Cf(Ct("") * (
      whitespace * P("[") * Cg(Cc("timestamp") * timestamp ) * P("]")
    * whitespace *          Cg(Cc("status"   ) * keysvalues)
),rawset)

patterns.keysvalues = keysvalues
patterns.statusline = statusline
patterns.timestamp  = timestamp

loggers = loggers or { }

local timeformat = format("[%%s%s]",os.timezone(true))
local dateformat = "!%Y-%m-%d %H:%M:%S"

function loggers.makeline(t)
    local result = { } -- minimize time that file is open
    result[#result+1] = format(timeformat,date(dateformat))
    for k, v in sortedpairs(t) do
        local tv = type(v)
        if tv == "string" then
            if v ~= "password" then
                result[#result+1] = format(" %s=%q",k,v)
            end
        elseif tv == "number" or tv == "boolean" then
            result[#result+1] = format(" %s=%q",k,tostring(v))
        end
    end
    return concat(result," ")
end

local function append(filename,...)
    local f = openfile(filename,"a+")
    if not f then
        dir.mkdirs(file.dirname(filename))
        f = openfile(filename,"a+")
    end
    if f then
        f:write(...)
        f:close()
        return true
    else
        return false
    end
end

function loggers.store(filename,data) -- a log service is nicer
    if type(data) == "table"then
        data = loggers.makeline(data)
    end
    pcall(append,filename,data,"\n")
end

function loggers.collect(filename,result)
    if lfs.isfile(filename) then
        local r = lpegmatch(Ct(statusline^0),io.loaddata(filename))
        if result then -- append
            local nofresult = #result
            for i=1,#r do
                nofresult = nofresult + 1
                result[nofresult] = r[i]
            end
            return result
        else
            return r
        end
    else
        return result or { }
    end
end

function loggers.fields(results) -- returns hash of fields with counts so that we can decide on importance
    local fields = { }
    if results then
        for i=1,#results do
            local r = results[i]
            for k, v in next, r do
                local f = fields[k]
                if not f then
                    fields[k] = 1
                else
                    fields[k] = f + 1
                end
            end
        end
    end
    return fields
end

local template = [[<!-- log entries: begin --!>
<table>
<tr>%s</tr>
%s
</table>
<!-- log entries: end --!>
]]

function loggers.tohtml(entries,fields)
    if not fields or #fields == 0 then
        return ""
    end
    if type(entries) == "string" then
        entries = loggers.collect(entries)
    end
    local scratch, lines = { }, { }
    for i=1,#entries do
        local entry = entries[i]
        local status = entry.status
        for i=1,#fields do
            local field = fields[i]
            local v = status[field.name]
            if v ~= nil then
                v = tostring(v)
                local f = field.format
                if f then
                    v = format(f,v)
                end
                scratch[i] = format("<td nowrap='nowrap' align='%s'>%s</td>",field.align or "left",v)
            else
                scratch[i] = "<td/>"
            end
        end
        lines[i] = format("<tr>%s</tr>",concat(scratch))
    end
    for i=1,#fields do
        local field = fields[i]
        scratch[i] = format("<th nowrap='nowrap' align='left'>%s</th>", field.label or field.name)
    end
    local result = format(template,concat(scratch),concat(lines,"\n"))
    return result, entries
end

-- loggers.store("test.log", { name = "whatever", more = math.random(1,100) })

-- local fields = {
--     { name = "name", align = "left"  },
--     { name = "more", align = "right" },
-- }

-- local entries = loggers.collect("test.log")
-- local html    = loggers.tohtml(entries,fields)

-- inspect(entries)
-- inspect(fields)
-- inspect(html)