summaryrefslogtreecommitdiff
path: root/tex/context/base/data-sch.lua
blob: 2327227496e8a7be955d666248e29a59e9cef54a (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
if not modules then modules = { } end modules ['data-sch'] = {
    version   = 1.001,
    comment   = "companion to luat-lib.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

local http  = require("socket.http")
local ltn12 = require("ltn12")
local gsub, concat, format = string.gsub, table.concat, string.format
local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders

local trace_schemes = false  trackers.register("resolvers.schemes",function(v) trace_schemes = v end)

local report_schemes = logs.new("schemes")

local resolvers = resolvers

resolvers.schemes = resolvers.schemes or { }
local schemes     = resolvers.schemes
schemes.threshold = 24 * 60 * 60

directives.register("schemes.threshold", function(v) schemes.threshold = tonumber(v) or schemes.threshold end)

local cached, loaded, reused = { }, { }, { }

function schemes.curl(name,cachename) -- will use sockets instead or the curl library
    local command = "curl --silent --create-dirs --output " .. cachename .. " " .. name -- no protocol .. "://"
    os.spawn(command)
end

function schemes.fetch(protocol,name,handler)
    local cleanname = gsub(name,"[^%a%d%.]+","-")
    local cachename = caches.setfirstwritablefile(cleanname,"schemes")
    if not cached[name] then
        statistics.starttiming(schemes)
        if not io.exists(cachename) or (os.difftime(os.time(),lfs.attributes(cachename).modification) > schemes.threshold) then
            cached[name] = cachename
            if handler then
                if trace_schemes then
                    report_schemes("fetching '%s', protocol '%s', method 'built-in'",name,protocol)
                end
                io.flush()
                handler(protocol,name,cachename)
            else
                if trace_schemes then
                    report_schemes("fetching '%s', protocol '%s', method 'curl'",name,protocol)
                end
                io.flush()
                schemes.curl(name,cachename)
            end
        end
        if io.exists(cachename) then
            cached[name] = cachename
            if trace_schemes then
                report_schemes("using cached '%s', protocol '%s', cachename '%s'",name,protocol,cachename)
            end
        else
            cached[name] = ""
            if trace_schemes then
                report_schemes("using missing '%s', protocol '%s'",name,protocol)
            end
        end
        loaded[protocol] = loaded[protocol] + 1
        statistics.stoptiming(schemes)
    else
        if trace_schemes then
            report_schemes("reusing '%s', protocol '%s'",name,protocol)
        end
        reused[protocol] = reused[protocol] + 1
    end
    return cached[name]
end

function finders.schemes(protocol,filename,handler)
    local foundname = schemes.fetch(protocol,filename,handler)
    return finders.generic(protocol,foundname)
end

function openers.schemes(protocol,filename)
    return openers.generic(protocol,filename)
end

function loaders.schemes(protocol,filename)
    return loaders.generic(protocol,filename)
end

-- could be metatable

function schemes.install(protocol,handler)
    loaded [protocol] = 0
    reused [protocol] = 0
    finders[protocol] = function (filename,filetype) return finders.schemes(protocol,filename,handler) end
    openers[protocol] = function (filename)          return openers.schemes(protocol,filename)         end
    loaders[protocol] = function (filename)          return loaders.schemes(protocol,filename)         end
end

local function http_handler(protocol,name,cachename)
    local tempname = cachename .. ".tmp"
    local f = io.open(tempname,"wb")
    local status, message = http.request {
        url = name,
        sink = ltn12.sink.file(f)
    }
    if not status then
        os.remove(tempname)
    else
        os.remove(cachename)
        os.rename(tempname,cachename)
    end
end

schemes.install('http',http_handler)
schemes.install('https')
schemes.install('ftp')

statistics.register("scheme handling time", function()
    local l, r = { }, { }
    for k, v in table.sortedhash(loaded) do
        if v > 0 then
            l[#l+1] = k .. ":" .. v
        end
    end
    for k, v in table.sortedhash(reused) do
        if v > 0 then
            r[#r+1] = k .. ":" .. v
        end
    end
    local n = #l + #r
    if n > 0 then
        l = (#l > 0 and concat(l)) or "none"
        r = (#r > 0 and concat(r)) or "none"
        return format("%s seconds, %s processed, threshold %s seconds, loaded: %s, reused: %s",
            statistics.elapsedtime(schemes), n, schemes.threshold, l, r)
    else
        return nil
    end
end)

--~ trace_schemes = true
--~ print(schemes.fetch("http","http://www.pragma-ade.com/show-man.pdf",http_handler))