diff options
author | Hans Hagen <pragma@wxs.nl> | 2007-12-05 13:56:00 +0100 |
---|---|---|
committer | Hans Hagen <pragma@wxs.nl> | 2007-12-05 13:56:00 +0100 |
commit | 6312e2b2913bc7de6f3c0ba30b993e2b4714edf1 (patch) | |
tree | e0e90382ddb930a0b4f534824892235b343dcdc4 /scripts | |
parent | 19af23ac5cb927d986a64ac1dc52ed2d7bad2450 (diff) | |
download | context-6312e2b2913bc7de6f3c0ba30b993e2b4714edf1.tar.gz |
stable 2007.12.05 13:56
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/context/lua/luatools.lua | 865 | ||||
-rw-r--r-- | scripts/context/lua/mtx-babel.lua | 150 | ||||
-rw-r--r-- | scripts/context/lua/mtx-cache.lua | 8 | ||||
-rw-r--r-- | scripts/context/lua/mtx-chars.lua | 8 | ||||
-rw-r--r-- | scripts/context/lua/mtx-context.lua | 27 | ||||
-rw-r--r-- | scripts/context/lua/mtx-convert.lua | 86 | ||||
-rw-r--r-- | scripts/context/lua/mtx-fonts.lua | 9 | ||||
-rw-r--r-- | scripts/context/lua/mtx-watch.lua | 224 | ||||
-rw-r--r-- | scripts/context/lua/mtxrun.lua | 1292 | ||||
-rw-r--r-- | scripts/context/lua/scite-ctx.lua | 924 | ||||
-rw-r--r-- | scripts/context/ruby/base/kpse.rb | 8 | ||||
-rw-r--r-- | scripts/context/ruby/base/tex.rb | 29 | ||||
-rw-r--r-- | scripts/context/ruby/base/texutil.rb | 4 | ||||
-rw-r--r-- | scripts/context/ruby/graphics/gs.rb | 6 | ||||
-rw-r--r-- | scripts/context/ruby/texexec.rb | 15 | ||||
-rw-r--r-- | scripts/context/ruby/www/exa.rb | 1 | ||||
-rw-r--r-- | scripts/context/ruby/www/lib.rb | 8 |
17 files changed, 2166 insertions, 1498 deletions
diff --git a/scripts/context/lua/luatools.lua b/scripts/context/lua/luatools.lua index d53180cfa..1dc67519e 100644 --- a/scripts/context/lua/luatools.lua +++ b/scripts/context/lua/luatools.lua @@ -141,6 +141,10 @@ function string.piecewise(str, pat, fnc) -- variant of split for k in string.splitter(str,pat) do fnc(k) end end +--~ function string.piecewise(str, pat, fnc) -- variant of split +--~ for k in str:splitter(pat) do fnc(k) end +--~ end + --~ do if lpeg then --~ -- this alternative is 30% faster esp when we cache them @@ -158,7 +162,7 @@ end --~ split = lpeg.Ct(c*(p*c)^0) --~ splitters[separator] = split --~ end ---~ return lpeg.match(split,self) +--~ return lpeg.match(split,self) -- split:match(self) --~ else --~ return { } --~ end @@ -315,7 +319,7 @@ end --~ return self .. self.rep(chr or " ",n-#self) --~ end -function string:padd(n,chr) +function string:rpadd(n,chr) local m = n-#self if m > 0 then return self .. self.rep(chr or " ",m) @@ -324,6 +328,17 @@ function string:padd(n,chr) end end +function string:lpadd(n,chr) + local m = n-#self + if m > 0 then + return self.rep(chr or " ",m) .. self + else + return self + end +end + +string.padd = string.rpadd + function is_number(str) return str:find("^[%-%+]?[%d]-%.?[%d+]$") == 1 end @@ -349,6 +364,49 @@ function string:split_settings() -- no {} handling, see l-aux for lpeg variant end +-- filename : l-lpeg.lua +-- author : Hans Hagen, PRAGMA-ADE, Hasselt NL +-- copyright: PRAGMA ADE / ConTeXt Development Team +-- license : see context related readme files + +if not versions then versions = { } end versions['l-lpeg'] = 1.001 + +--~ l-lpeg.lua : + +--~ lpeg.digit = lpeg.R('09')^1 +--~ lpeg.sign = lpeg.S('+-')^1 +--~ lpeg.cardinal = lpeg.P(lpeg.sign^0 * lpeg.digit^1) +--~ lpeg.integer = lpeg.P(lpeg.sign^0 * lpeg.digit^1) +--~ lpeg.float = lpeg.P(lpeg.sign^0 * lpeg.digit^0 * lpeg.P('.') * lpeg.digit^1) +--~ lpeg.number = lpeg.float + lpeg.integer +--~ lpeg.oct = lpeg.P("0") * lpeg.R('07')^1 +--~ lpeg.hex = lpeg.P("0x") * (lpeg.R('09') + lpeg.R('AF'))^1 +--~ lpeg.uppercase = lpeg.P("AZ") +--~ lpeg.lowercase = lpeg.P("az") + +--~ lpeg.eol = lpeg.S('\r\n\f')^1 -- includes formfeed +--~ lpeg.space = lpeg.S(' ')^1 +--~ lpeg.nonspace = lpeg.P(1-lpeg.space)^1 +--~ lpeg.whitespace = lpeg.S(' \r\n\f\t')^1 +--~ lpeg.nonwhitespace = lpeg.P(1-lpeg.whitespace)^1 + +function lpeg.anywhere(pattern) --slightly adapted from website + return lpeg.P { lpeg.P(pattern) + 1 * lpeg.V(1) } +end + +function lpeg.startswith(pattern) --slightly adapted + return lpeg.P(pattern) +end + +--~ g = lpeg.splitter(" ",function(s) ... end) -- gmatch:lpeg = 3:2 + +function lpeg.splitter(pattern, action) + return (((1-lpeg.P(pattern))^1)/action+1)^0 +end + + + + -- filename : l-table.lua -- comment : split off from luat-lib -- author : Hans Hagen, PRAGMA-ADE, Hasselt NL @@ -422,6 +480,7 @@ function table.merge(t, ...) t[k] = v end end + return t end function table.merged(...) @@ -434,6 +493,25 @@ function table.merged(...) return tmp end +function table.imerge(t, ...) + for _, list in ipairs({...}) do + for k,v in ipairs(list) do + t[#t+1] = v + end + end + return t +end + +function table.imerged(...) + local tmp = { } + for _, list in ipairs({...}) do + for _,v in pairs(list) do + tmp[#tmp+1] = v + end + end + return tmp +end + if not table.fastcopy then function table.fastcopy(old) -- fast one @@ -441,11 +519,15 @@ if not table.fastcopy then local new = { } for k,v in pairs(old) do if type(v) == "table" then - new[k] = table.copy(v) + new[k] = table.fastcopy(v) -- was just table.copy else new[k] = v end end + local mt = getmetatable(old) + if mt then + setmetatable(new,mt) + end return new else return { } @@ -456,30 +538,32 @@ end if not table.copy then - function table.copy(t, _lookup_table) -- taken from lua wiki - _lookup_table = _lookup_table or { } + function table.copy(t, tables) -- taken from lua wiki, slightly adapted + tables = tables or { } local tcopy = {} - if not _lookup_table[t] then - _lookup_table[t] = tcopy + if not tables[t] then + tables[t] = tcopy end - for i,v in pairs(t) do + for i,v in pairs(t) do -- brrr, what happens with sparse indexed if type(i) == "table" then - if _lookup_table[i] then - i = _lookup_table[i] + if tables[i] then + i = tables[i] else - i = table.copy(i, _lookup_table) + i = table.copy(i, tables) end end if type(v) ~= "table" then tcopy[i] = v + elseif tables[v] then + tcopy[i] = tables[v] else - if _lookup_table[v] then - tcopy[i] = _lookup_table[v] - else - tcopy[i] = table.copy(v, _lookup_table) - end + tcopy[i] = table.copy(v, tables) end end + local mt = getmetatable(t) + if mt then + setmetatable(tcopy,mt) + end return tcopy end @@ -514,6 +598,8 @@ end do + -- one of my first exercises in lua ... + -- 34.055.092 32.403.326 arabtype.tma -- 1.620.614 1.513.863 lmroman10-italic.tma -- 1.325.585 1.233.044 lmroman10-regular.tma @@ -873,6 +959,25 @@ function table.tohash(t) return h end +function table.contains(t, v) + if t then + for i=1, #t do + if t[i] == v then + return true + end + end + end + return false +end + +function table.count(t) + local n, e = 0, next(t) + while e do + n, e = n + 1, next(t,e) + end + return n +end + --~ function table.are_equal(a,b) --~ return table.serialize(a) == table.serialize(b) --~ end @@ -1053,6 +1158,38 @@ do end +function io.ask(question,default,options) + while true do + io.write(question) + if options then + io.write(string.format(" [%s]",table.concat(options,"|"))) + end + if default then + io.write(string.format(" [%s]",default)) + end + io.write(string.format(" ")) + local answer = io.read() + answer = answer:gsub("^%s*(.*)%s*$","%1") + if answer == "" and default then + return default + elseif not options then + return answer + else + for _,v in pairs(options) do + if v == answer then + return answer + end + end + local pattern = "^" .. answer + for _,v in pairs(options) do + if v:find(pattern) then + return v + end + end + end + end +end + -- filename : l-number.lua -- comment : split off from luat-lib @@ -1064,6 +1201,31 @@ if not versions then versions = { } end versions['l-number'] = 1.001 if not number then number = { } end +-- a,b,c,d,e,f = number.toset(100101) + +function number.toset(n) + return (tostring(n)):match("(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?)") +end + +-- the lpeg way is slower on 8 digits, but faster on 4 digits, some 7.5% +-- on +-- +-- for i=1,1000000 do +-- local a,b,c,d,e,f,g,h = number.toset(12345678) +-- local a,b,c,d = number.toset(1234) +-- local a,b,c = number.toset(123) +-- end +-- +-- of course dedicated "(.)(.)(.)(.)" matches are even faster + +do + local one = lpeg.C(1-lpeg.S(''))^1 + + function number.toset(n) + return lpeg.match(one,tostring(n)) + end +end + -- filename : l-os.lua @@ -1110,7 +1272,7 @@ if md5 then do if not md5.HEX then function md5.HEX(str) return convert(str,"%02X") end end if not md5.hex then function md5.hex(str) return convert(str,"%02x") end end - if not md5.dec then function md5.dec(str) return convert(stt,"%03i") end end + if not md5.dec then function md5.dec(str) return convert(str,"%03i") end end end end @@ -1138,7 +1300,7 @@ function file.addsuffix(filename, suffix) end function file.replacesuffix(filename, suffix) - return filename:gsub("%.%a+$", "." .. suffix) + return (filename:gsub("%.%a+$", "." .. suffix)) end function file.dirname(name) @@ -1150,7 +1312,7 @@ function file.basename(name) end function file.extname(name) - return name:match("^.+%.(.-)$") or "" + return name:match("^.+%.([^/\\]-)$") or "" end function file.join(...) @@ -1252,15 +1414,18 @@ dir = { } if lfs then function dir.glob_pattern(path,patt,recurse,action) - for name in lfs.dir(path) do - local full = path .. '/' .. name - local mode = lfs.attributes(full,'mode') - if mode == 'file' then - if name:find(patt) then - action(full) + local ok, scanner = xpcall(function() return lfs.dir(path) end, function() end) -- kepler safe + if ok and type(scanner) == "function" then + for name in scanner do + local full = path .. '/' .. name + local mode = lfs.attributes(full,'mode') + if mode == 'file' then + if name:find(patt) then + action(full) + end + elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then + dir.glob_pattern(full,patt,recurse,action) end - elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then - dir.glob_pattern(full,patt,recurse,action) end end end @@ -1285,6 +1450,30 @@ if lfs then return t end + function dir.globfiles(path,recurse,func,files) + if type(func) == "string" then + local s = func -- alas, we need this indirect way + func = function(name) return name:find(s) end + end + files = files or { } + for name in lfs.dir(path) do + if name:find("^%.") then + --- skip + elseif lfs.attributes(name,'mode') == "directory" then + if recurse then + dir.globfiles(path .. "/" .. name,recurse,func,files) + end + elseif func then + if func(name) then + files[#files+1] = path .. "/" .. name + end + else + files[#files+1] = path .. "/" .. name + end + end + return files + end + -- t = dir.glob("c:/data/develop/context/sources/**/????-*.tex") -- t = dir.glob("c:/data/develop/tex/texmf/**/*.tex") -- t = dir.glob("c:/data/develop/context/texmf/**/*.tex") @@ -1346,11 +1535,21 @@ function boolean.tonumber(b) if b then return 1 else return 0 end end -function toboolean(str) - if type(str) == "string" then - return str == "true" or str == "yes" or str == "on" or str == "1" - elseif type(str) == "number" then - return tonumber(str) ~= 0 +function toboolean(str,tolerant) + if tolerant then + if type(str) == "string" then + return str == "true" or str == "yes" or str == "on" or str == "1" + elseif type(str) == "number" then + return tonumber(str) ~= 0 + elseif type(str) == "nil" then + return false + else + return str + end + elseif str == "true" then + return true + elseif str == "false" then + return false else return str end @@ -1641,10 +1840,15 @@ function utils.merger.selfclean(name) ) end +utils.lua.compile_strip = true + function utils.lua.compile(luafile, lucfile) -- utils.report("compiling",luafile,"into",lucfile) os.remove(lucfile) - local command = "-s -o " .. string.quote(lucfile) .. " " .. string.quote(luafile) + local command = "-o " .. string.quote(lucfile) .. " " .. string.quote(luafile) + if utils.lua.compile_strip then + command = "-s " .. command + end if os.execute("texluac " .. command) == 0 then return true elseif os.execute("luac " .. command) == 0 then @@ -1742,6 +1946,10 @@ function environment.showarguments() end end +function environment.setargument(name,value) + environment.arguments[name] = value +end + function environment.argument(name) if environment.arguments[name] then return environment.arguments[name] @@ -1823,6 +2031,7 @@ end -- Beware, loading and saving is overloaded in luat-tmp! -- todo: instances.[hashes,cnffiles,configurations,522] -> ipairs (alles check, sneller) +-- todo: check escaping in find etc, too much, too slow if not versions then versions = { } end versions['luat-inp'] = 1.001 if not environment then environment = { } end @@ -2060,7 +2269,7 @@ input.settrace(tonumber(os.getenv("MTX.INPUT.TRACE") or os.getenv("MTX_INPUT_TRA -- These functions can be used to test the performance, especially -- loading the database files. -function input.start_timing(instance) +function input.starttiming(instance) if instance then instance.starttime = os.clock() if not instance.loadtime then @@ -2069,7 +2278,7 @@ function input.start_timing(instance) end end -function input.stop_timing(instance, report) +function input.stoptiming(instance, report) if instance and instance.starttime then instance.stoptime = os.clock() local loadtime = instance.stoptime - instance.starttime @@ -2083,9 +2292,6 @@ function input.stop_timing(instance, report) end end -input.stoptiming = input.stop_timing -input.starttiming = input.start_timing - function input.elapsedtime(instance) return string.format("%0.3f",instance.loadtime or 0) end @@ -2398,99 +2604,106 @@ function input.generatedatabase(instance,specification) return input.methodhandler('generators', instance, specification) end -function input.generators.tex(instance,specification) - local tag = specification - if not instance.lsrmode and lfs and lfs.dir then - input.report("scanning path",specification) - instance.files[tag] = { } - local files = instance.files[tag] - local n, m, r = 0, 0, 0 - local spec = specification .. '/' - local attributes = lfs.attributes - local directory = lfs.dir - local small = instance.smallcache - local function action(path) - local mode, full - if path then - full = spec .. path .. '/' - else - full = spec - end - for name in directory(full) do - if name:find("^%.") then - -- skip - elseif name:find("[%~%`%!%#%$%%%^%&%*%(%)%=%{%}%[%]%:%;\"\'%|%|%<%>%,%?\n\r\t]") then - -- texio.write_nl("skipping " .. name) - -- skip +do + + local weird = lpeg.anywhere(lpeg.S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t")) + + function input.generators.tex(instance,specification) + local tag = specification + if not instance.lsrmode and lfs and lfs.dir then + input.report("scanning path",specification) + instance.files[tag] = { } + local files = instance.files[tag] + local n, m, r = 0, 0, 0 + local spec = specification .. '/' + local attributes = lfs.attributes + local directory = lfs.dir + local small = instance.smallcache + local function action(path) + local mode, full + if path then + full = spec .. path .. '/' else - mode = attributes(full..name,'mode') - if mode == "directory" then - m = m + 1 - if path then - action(path..'/'..name) - else - action(name) - end - elseif path and mode == 'file' then - n = n + 1 - local f = files[name] - if f then - if not small then - if type(f) == 'string' then - files[name] = { f, path } - else - f[#f+1] = path - end + full = spec + end + for name in directory(full) do + if name:find("^%.") then + -- skip + -- elseif name:find("[%~%`%!%#%$%%%^%&%*%(%)%=%{%}%[%]%:%;\"\'%|%<%>%,%?\n\r\t]") then -- too much escaped + elseif weird:match(name) then + -- texio.write_nl("skipping " .. name) + -- skip + else + mode = attributes(full..name,'mode') + if mode == "directory" then + m = m + 1 + if path then + action(path..'/'..name) + else + action(name) end - else - files[name] = path - local lower = name:lower() - if name ~= lower then - files["remap:"..lower] = name - r = r + 1 + elseif path and mode == 'file' then + n = n + 1 + local f = files[name] + if f then + if not small then + if type(f) == 'string' then + files[name] = { f, path } + else + f[#f+1] = path + end + end + else + files[name] = path + local lower = name:lower() + if name ~= lower then + files["remap:"..lower] = name + r = r + 1 + end end end end end end - end - action() - input.report(string.format("%s files found on %s directories with %s uppercase remappings",n,m,r)) - else - local fullname = file.join(specification,input.lsrname) - local path = '.' - local f = io.open(fullname) - if f then - instance.files[tag] = { } - local files = instance.files[tag] - local small = instance.smallcache - input.report("loading lsr file",fullname) - -- for line in f:lines() do -- much slower then the next one - for line in (f:read("*a")):gmatch("(.-)\n") do - if line:find("^[%a%d]") then - local fl = files[line] - if fl then - if not small then - if type(fl) == 'string' then - files[line] = { fl, path } -- table - else - fl[#fl+1] = path + action() + input.report(string.format("%s files found on %s directories with %s uppercase remappings",n,m,r)) + else + local fullname = file.join(specification,input.lsrname) + local path = '.' + local f = io.open(fullname) + if f then + instance.files[tag] = { } + local files = instance.files[tag] + local small = instance.smallcache + input.report("loading lsr file",fullname) + -- for line in f:lines() do -- much slower then the next one + for line in (f:read("*a")):gmatch("(.-)\n") do + if line:find("^[%a%d]") then + local fl = files[line] + if fl then + if not small then + if type(fl) == 'string' then + files[line] = { fl, path } -- table + else + fl[#fl+1] = path + end + end + else + files[line] = path -- string + local lower = line:lower() + if line ~= lower then + files["remap:"..lower] = line end end else - files[line] = path -- string - local lower = line:lower() - if line ~= lower then - files["remap:"..lower] = line - end + path = line:match("%.%/(.-)%:$") or path -- match could be nil due to empty line end - else - path = line:match("%.%/(.-)%:$") or path -- match could be nil due to empty line end + f:close() end - f:close() end end + end -- savers, todo @@ -2790,7 +3003,7 @@ end function input.list_configurations(instance) for _,key in pairs(table.sortedkeys(instance.kpsevars)) do - if not instance.pattern or instance.pattern == "" or key:find(instance.pattern) then + if not instance.pattern or (instance.pattern=="") or key:find(instance.pattern) then print(key.."\n") for i,c in ipairs(instance.order) do local str = c[key] @@ -2913,10 +3126,168 @@ end -- a,b,c/{p,q,r}/d/{x,y,z}// -- a,b,c/{p,q/{x,y,z},r},d/{p,q,r} -- a,b,c/{p,q/{x,y,z},r},d/{p,q,r} +-- a{b,c}{d,e}f +-- {a,b,c,d} +-- {a,b,c/{p,q,r},d} +-- {a,b,c/{p,q,r}/d/{x,y,z}//} +-- {a,b,c/{p,q/{x,y,z}},d/{p,q,r}} +-- {a,b,c/{p,q/{x,y,z},w}v,d/{p,q,r}} + +-- this one is better and faster, but it took me a while to realize +-- that this kind of replacement is cleaner than messy parsing and +-- fuzzy concatenating we can probably gain a bit with selectively +-- applying lpeg, but experiments with lpeg parsing this proved not to +-- work that well; the parsing is ok, but dealing with the resulting +-- table is a pain because we need to work inside-out recursively + +--~ function input.aux.splitpathexpr(str, t, validate) +--~ -- no need for optimization, only called a few times, we can use lpeg for the sub +--~ t = t or { } +--~ while true do +--~ local done = false +--~ while true do +--~ ok = false +--~ str = str:gsub("([^{},]+){([^{}]-)}", function(a,b) +--~ local t = { } +--~ for s in b:gmatch("([^,]+)") do +--~ t[#t+1] = a .. s +--~ end +--~ ok, done = true, true +--~ return "{" .. table.concat(t,",") .. "}" +--~ end) +--~ if not ok then break end +--~ end +--~ while true do +--~ ok = false +--~ str = str:gsub("{([^{}]-)}([^{},]+)", function(a,b) +--~ local t = { } +--~ for s in a:gmatch("([^,]+)") do +--~ t[#t+1] = s .. b +--~ end +--~ ok, done = true, true +--~ return "{" .. table.concat(t,",") .. "}" +--~ end) +--~ if not ok then break end +--~ end +--~ while true do +--~ ok = false +--~ str = str:gsub("([,{]){([^{}]+)}([,}])", function(a,b,c) +--~ ok, done = true, true +--~ return a .. b .. c +--~ end) +--~ if not ok then break end +--~ end +--~ if not done then break end +--~ end +--~ while true do +--~ ok = false +--~ str = str:gsub("{([^{}]-)}{([^{}]-)}", function(a,b) +--~ local t = { } +--~ for sa in a:gmatch("([^,]+)") do +--~ for sb in b:gmatch("([^,]+)") do +--~ t[#t+1] = sa .. sb +--~ end +--~ end +--~ ok = true +--~ return "{" .. table.concat(t,",") .. "}" +--~ end) +--~ if not ok then break end +--~ end +--~ while true do +--~ ok = false +--~ str = str:gsub("{([^{}]-)}", function(a) +--~ ok = true +--~ return a +--~ end) +--~ if not ok then break end +--~ end +--~ if validate then +--~ for s in str:gmatch("([^,]+)") do +--~ s = validate(s) +--~ if s then t[#t+1] = s end +--~ end +--~ else +--~ for s in str:gmatch("([^,]+)") do +--~ t[#t+1] = s +--~ end +--~ end +--~ return t +--~ end + +function input.aux.splitpathexpr(str, t, validate) + -- no need for optimization, only called a few times, we can use lpeg for the sub + t = t or { } + local concat = table.concat + while true do + local done = false + while true do + ok = false + str = str:gsub("([^{},]+){([^{}]-)}", function(a,b) + local t = { } + b:piecewise(",", function(s) t[#t+1] = a .. s end) + ok, done = true, true + return "{" .. concat(t,",") .. "}" + end) + if not ok then break end + end + while true do + ok = false + str = str:gsub("{([^{}]-)}([^{},]+)", function(a,b) + local t = { } + a:piecewise(",", function(s) t[#t+1] = s .. b end) + ok, done = true, true + return "{" .. concat(t,",") .. "}" + end) + if not ok then break end + end + while true do + ok = false + str = str:gsub("([,{]){([^{}]+)}([,}])", function(a,b,c) + ok, done = true, true + return a .. b .. c + end) + if not ok then break end + end + if not done then break end + end + while true do + ok = false + str = str:gsub("{([^{}]-)}{([^{}]-)}", function(a,b) + local t = { } + a:piecewise(",", function(sa) + b:piecewise(",", function(sb) + t[#t+1] = sa .. sb + end) + end) + ok = true + return "{" .. concat(t,",") .. "}" + end) + if not ok then break end + end + while true do + ok = false + str = str:gsub("{([^{}]-)}", function(a) + ok = true + return a + end) + if not ok then break end + end + if validate then + str:piecewise(",", function(s) + s = validate(s) + if s then t[#t+1] = s end + end) + else + str:piecewise(",", function(s) + t[#t+1] = s + end) + end + return t +end function input.aux.expanded_path(instance,pathlist) -- a previous version fed back into pathlist - local i, n, oldlist, newlist, ok = 0, 0, { }, { }, false + local newlist, ok = { }, false for _,v in ipairs(pathlist) do if v:find("[{}]") then ok = true @@ -2924,45 +3295,11 @@ function input.aux.expanded_path(instance,pathlist) end end if ok then - for _,v in ipairs(pathlist) do - oldlist[#oldlist+1] = (v:gsub("([\{\}])", function(p) - if p == "{" then - i = i + 1 - if i > n then n = i end - return "<" .. (i-1) .. ">" - else - i = i - 1 - return "</" .. i .. ">" - end - end)) - end - for i=1,n do - while true do - local more = false - local pattern = "^(.-)<"..(n-i)..">(.-)</"..(n-i)..">(.-)$" - local t = { } - for _,v in ipairs(oldlist) do - local pre, mid, post = v:match(pattern) - if pre and mid and post then - more = true - for vv in string.gmatch(mid..',',"(.-),") do - if vv == '.' then - t[#t+1] = pre..post - else - t[#t+1] = pre..vv..post - end - end - else - t[#t+1] = v - end - end - oldlist = t - if not more then break end - end - end - for _,v in ipairs(oldlist) do - v = file.collapse_path(v) - if v ~= "" and not v:find(instance.dummy_path_expr) then newlist[#newlist+1] = v end + for _, v in ipairs(pathlist) do + input.aux.splitpathexpr(v, newlist, function(s) + s = file.collapse_path(s) + return s ~= "" and not s:find(instance.dummy_path_expr) and s + end) end else for _,v in ipairs(pathlist) do @@ -2975,6 +3312,83 @@ function input.aux.expanded_path(instance,pathlist) return newlist end +--~ old one, imperfect and not that efficient +--~ +--~ function input.aux.expanded_path(instance,pathlist) +--~ -- a previous version fed back into pathlist +--~ local i, n, oldlist, newlist, ok = 0, 0, { }, { }, false +--~ for _,v in ipairs(pathlist) do +--~ if v:find("[{}]") then +--~ ok = true +--~ break +--~ end +--~ end +--~ if ok then +--~ for _,v in ipairs(pathlist) do +--~ oldlist[#oldlist+1] = (v:gsub("([\{\}])", function(p) +--~ if p == "{" then +--~ i = i + 1 +--~ if i > n then n = i end +--~ return "<" .. (i-1) .. ">" +--~ else +--~ i = i - 1 +--~ return "</" .. i .. ">" +--~ end +--~ end)) +--~ end +--~ for i=1,n do +--~ while true do +--~ local more = false +--~ local pattern = "^(.-)<"..(n-i)..">(.-)</"..(n-i)..">(.-)$" +--~ local t = { } +--~ for _,v in ipairs(oldlist) do +--~ local pre, mid, post = v:match(pattern) +--~ if pre and mid and post then +--~ more = true +--~ for vv in string.gmatch(mid..',',"(.-),") do -- (mid, "([^,]+)") +--~ if vv == '.' then +--~ t[#t+1] = pre..post +--~ else +--~ t[#t+1] = pre..vv..post +--~ end +--~ end +--~ else +--~ t[#t+1] = v +--~ end +--~ end +--~ oldlist = t +--~ if not more then break end +--~ end +--~ end +--~ if true then +--~ -- many dups are possible due to messy resolve / order can be messed up too, brr ! +--~ local ok = { } +--~ for _,o in ipairs(oldlist) do +--~ for v in o:gmatch("([^,]+)") do +--~ if not ok[v] then +--~ ok[v] = true +--~ v = file.collapse_path(v) +--~ if v ~= "" and not v:find(instance.dummy_path_expr) then newlist[#newlist+1] = v end +--~ end +--~ end +--~ end +--~ else +--~ for _,v in ipairs(oldlist) do +--~ v = file.collapse_path(v) +--~ if v ~= "" and not v:find(instance.dummy_path_expr) then newlist[#newlist+1] = v end +--~ end +--~ end +--~ else +--~ for _,v in ipairs(pathlist) do +--~ for vv in string.gmatch(v..',',"(.-),") do +--~ vv = file.collapse_path(v) +--~ if vv ~= "" then newlist[#newlist+1] = vv end +--~ end +--~ end +--~ end +--~ return newlist +--~ end + --~ function input.is_readable(name) -- brrr, get rid of this --~ return name:find("^zip##") or file.is_readable(name) --~ end @@ -3073,24 +3487,51 @@ function input.suffixes_of_format(str) end end -function input.aux.qualified_path(filename) -- make platform dependent / not good yet - return - filename:find("^%.+/") or - filename:find("^/") or - filename:find("^%a+%:") or - filename:find("^%a+##") -end +--~ function input.aux.qualified_path(filename) -- make platform dependent / not good yet +--~ return +--~ filename:find("^%.+/") or +--~ filename:find("^/") or +--~ filename:find("^%a+%:") or +--~ filename:find("^%a+##") +--~ end + +--~ function input.normalize_name(original) +--~ -- internally we use type##spec##subspec ; this hackery slightly slows down searching +--~ local str = original or "" +--~ str = str:gsub("::", "##") -- :: -> ## +--~ str = str:gsub("^(%a+)://" ,"%1##") -- zip:// -> zip## +--~ str = str:gsub("(.+)##(.+)##/(.+)","%1##%2##%3") -- ##/spec -> ##spec +--~ if (input.trace>1) and (original ~= str) then +--~ input.logger('= normalizer',original.." -> "..str) +--~ end +--~ return str +--~ end -function input.normalize_name(original) - -- internally we use type##spec##subspec ; this hackery slightly slows down searching - local str = original or "" - str = str:gsub("::", "##") -- :: -> ## - str = str:gsub("^(%a+)://" ,"%1##") -- zip:// -> zip## - str = str:gsub("(.+)##(.+)##/(.+)","%1##%2##%3") -- ##/spec -> ##spec - if (input.trace>1) and (original ~= str) then - input.logger('= normalizer',original.." -> "..str) +do -- called about 700 times for an empty doc (font initializations etc) + -- i need to weed the font files for redundant calls + + local letter = lpeg.R("az","AZ") + local separator = lpeg.P("##") + + local qualified = lpeg.P(".")^0 * lpeg.P("/") + letter*lpeg.P(":") + letter^1*separator + local normalized = lpeg.Cs( + (letter^1*(lpeg.P("://")/"##") * (1-lpeg.P(false))^1) + + (lpeg.P("::")/"##" + (1-separator)^1*separator*(1-separator)^1*separator*(lpeg.P("/")/"") + 1)^0 + ) + + -- ./name ../name /name c: zip## (todo: use url internally and get rid of ##) + function input.aux.qualified_path(filename) + return qualified:match(filename) + end + + -- zip:// -> zip## ; :: -> ## ; aa##bb##/cc -> aa##bb##cc + function input.normalize_name(original) + local str = normalized:match(original or "") + if input.trace > 1 and original ~= str then + input.logger('= normalizer',original.." -> "..str) + end + return str end - return str end -- split the next one up, better for jit @@ -3455,13 +3896,13 @@ function input.automount(instance) end function input.load(instance) - input.start_timing(instance) + input.starttiming(instance) input.identify_cnf(instance) input.load_cnf(instance) input.expand_variables(instance) input.load_hash(instance) input.automount(instance) - input.stop_timing(instance) + input.stoptiming(instance) end function input.for_files(instance, command, files, filetype, mustexist) @@ -3755,7 +4196,7 @@ being written at the same time is small. We also need to extend luatools with a recache feature.</p> --ldx]]-- -caches = caches or { } +caches = caches or { } dir = dir or { } texmf = texmf or { } @@ -3768,8 +4209,18 @@ caches.tree = false caches.temp = caches.temp or os.getenv("TEXMFCACHE") or os.getenv("HOME") or os.getenv("HOMEPATH") or os.getenv("VARTEXMF") or os.getenv("TEXMFVAR") or os.getenv("TMP") or os.getenv("TEMP") or os.getenv("TMPDIR") or nil caches.paths = caches.paths or { caches.temp } +input.usecache = not toboolean(os.getenv("TEXMFSHARECACHE") or "false",true) -- true + +if caches.temp and caches.temp ~= "" and lfs.attributes(caches.temp,"mode") ~= "directory" then + if io.ask(string.format("Should I create the cache path %s?",caches.temp), "no", { "yes", "no" }) == "yes" then + lfs.mkdir(caches.temp) + end +end if not caches.temp or caches.temp == "" then - print("\nFATAL ERROR: NO VALID TEMPORARY PATH\n") + print("\nfatal error: there is no valid cache path defined\n") + os.exit() +elseif lfs.attributes(caches.temp,"mode") ~= "directory" then + print(string.format("\nfatal error: cache path %s is not a directory\n",caches.temp)) os.exit() end @@ -3909,8 +4360,8 @@ do -- local report function containers.is_valid(container, name) if name and name ~= "" then - local cs = container.storage[name] - return cs and not table.is_empty(cs) and cs.cache_version == container.version + local storage = container.storage[name] + return storage and not table.is_empty(storage) and storage.cache_version == container.version else return false end @@ -3956,8 +4407,6 @@ end -- since we want to use the cache instead of the tree, we will now -- reimplement the saver. -input.usecache = true - function input.aux.save_data(instance, dataname, check) for cachename, files in pairs(instance[dataname]) do local name @@ -4357,7 +4806,7 @@ else function input.registerzipfile(instance,zipname,tag) if not zip.registeredfiles[zipname] then - input.start_timing(instance) + input.starttiming(instance) local z = zip.open(zipname) if not z then zipname = input.find_file(instance,zipname) @@ -4370,7 +4819,7 @@ else else input.logger("? zipfile","unknown "..zipname) end - input.stop_timing(instance) + input.stoptiming(instance) end end @@ -4476,7 +4925,7 @@ if texconfig and not texlua then t = { utftype = u, -- may go away lines = l, - current = 0, + current = 0, -- line number, not really needed handle = nil, noflines = #l, close = function() @@ -4484,15 +4933,23 @@ if texconfig and not texlua then input.show_close(filename) end, reader = function(self) - if not self then self = t end - if self.current >= #self.lines then + self = self or t + local current, lines = self.current, self.lines + if current >= #lines then return nil else - self.current = self.current + 1 - if input.filters.utf_translator then - return input.filters.utf_translator(self.lines[t.current]) + self.current = current + 1 + local line = lines[self.current] + if line == "" then + return "" else - return self.lines[self.current] + local translator = input.filters.utf_translator + -- return (translator and translator(line)) or line + if translator then + return translator(line) + else + return line + end end end end @@ -4502,13 +4959,15 @@ if texconfig and not texlua then -- todo: file;name -> freeze / eerste regel scannen -> freeze t = { reader = function(self) - if not self then self = t end -- not used - if input.filters.dynamic_translator then - return input.filters.dynamic_translator(file_handle:read()) + local line = file_handle:read() + if line == "" then + return "" elseif input.filters.utf_translator then - return input.filters.utf_translator(file_handle:read()) + return input.filters.utf_translator(line) + elseif input.filters.dynamic_translator then + return input.filters.dynamic_translator(line) else - return file_handle:read() + return line end end, close = function() @@ -4745,7 +5204,7 @@ if texconfig and not texlua then luatex.variablenames = { 'main_memory', 'extra_mem_bot', 'extra_mem_top', - 'buf_size', + 'buf_size','expand_depth', 'font_max', 'font_mem_size', 'hash_extra', 'max_strings', 'pool_free', 'pool_size', 'string_vacancies', 'obj_tab_size', 'pdf_mem_size', 'dest_names_size', @@ -4920,6 +5379,7 @@ own = { } own.libs = { -- todo: check which ones are really needed 'l-string.lua', + 'l-lpeg.lua', 'l-table.lua', 'l-io.lua', 'l-number.lua', @@ -4991,7 +5451,7 @@ input.banner = 'LuaTools | ' utils.report = input.report input.defaultlibs = { -- not all are needed - 'l-string.lua', 'l-table.lua', 'l-boolean.lua', 'l-number.lua', 'l-unicode.lua', + 'l-string.lua', 'l-lpeg.lua', 'l-table.lua', 'l-boolean.lua', 'l-number.lua', 'l-unicode.lua', 'l-md5.lua', 'l-os.lua', 'l-io.lua', 'l-file.lua', 'l-dir.lua', 'l-utils.lua', 'l-tex.lua', 'luat-lib.lua', 'luat-inp.lua', 'luat-tmp.lua', 'luat-zip.lua', 'luat-tex.lua' } @@ -5153,7 +5613,8 @@ function input.my_make_format(instance,texname) flags[#flags+1] = "--lua=" .. string.quote(luaname) -- flags[#flags+1] = "--progname=" .. instance.progname -- potential fallback end - local command = "luatex ".. table.concat(flags," ") .. " " .. string.quote(fullname) .. " \\dump" + local bs = (environment.platform == "unix" and "\\\\") or "\\" -- todo: make a function + local command = "luatex ".. table.concat(flags," ") .. " " .. string.quote(fullname) .. " " .. bs .. "dump" input.report("running command: " .. command .. "\n") os.exec(command) end @@ -5163,7 +5624,7 @@ function input.my_make_format(instance,texname) end end -function input.my_run_format(instance,name,data) +function input.my_run_format(instance,name,data,more) -- hm, rather old code here; we can now use the file.whatever functions if name and (name ~= "") then local barename = name:gsub("%.%a+$","") @@ -5189,7 +5650,7 @@ function input.my_run_format(instance,name,data) if f then f:close() -- bug, no .fmt ! - local command = "luatex --fmt=" .. string.quote(barename) .. " --lua=" .. string.quote(luaname) .. " " .. string.quote(data) + local command = "luatex --fmt=" .. string.quote(barename) .. " --lua=" .. string.quote(luaname) .. " " .. string.quote(data) .. " " .. string.quote(more) input.report("running command: " .. command) os.exec(command) else @@ -5227,11 +5688,11 @@ elseif environment.arguments["find-path"] then elseif environment.arguments["run"] then input.my_prepare_a(instance) -- ! no need for loading databases input.verbose = true - input.my_run_format(instance,environment.files[1] or "",environment.files[2] or "") + input.my_run_format(instance,environment.files[1] or "",environment.files[2] or "",environment.files[3] or "") elseif environment.arguments["fmt"] then input.my_prepare_a(instance) -- ! no need for loading databases input.verbose = true - input.my_run_format(instance,environment.arguments["fmt"], environment.files[1] or "") + input.my_run_format(instance,environment.arguments["fmt"], environment.files[1] or "",environment.files[2] or "") elseif environment.arguments["expand-braces"] then input.my_prepare_a(instance) input.for_files(instance, input.expand_braces, environment.files) diff --git a/scripts/context/lua/mtx-babel.lua b/scripts/context/lua/mtx-babel.lua index 5ef9ae934..c9855b86a 100644 --- a/scripts/context/lua/mtx-babel.lua +++ b/scripts/context/lua/mtx-babel.lua @@ -1,6 +1,12 @@ --- data tables by Thomas A. Schmitz +if not modules then modules = { } end modules ['mtx-babel'] = { + version = 1.001, + comment = "companion to mtxrun.lua", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -dofile(input.find_file(instance,"luat-log.lua")) +-- data tables by Thomas A. Schmitz texmf.instance = instance -- we need to get rid of this / maybe current instance in global table @@ -9,6 +15,10 @@ scripts.babel = scripts.babel or { } do + local converters = { } + + -- greek + local replace_01 = { -- <' * | a = "ᾅ", h = "ᾕ", @@ -216,6 +226,7 @@ do O = "Ὁ", U = "Ὑ", W = "Ὡ", + R = "Ῥ", } local replace_23 = { -- > * @@ -301,61 +312,111 @@ do local skips_01 = lpeg.P("\\") * lpeg.R("az", "AZ")^1 local skips_02 = lpeg.P("[") * (1- lpeg.S("[]"))^1 * lpeg.P("]") - local stage_01 = (lpeg.P("<'") * lpeg.Cs(1) * lpeg.P('|')) / replace_01 - local stage_02 = (lpeg.P(">'") * lpeg.Cs(1) * lpeg.P('|')) / replace_02 - local stage_03 = (lpeg.P("<`") * lpeg.Cs(1) * lpeg.P('|')) / replace_03 - local stage_04 = (lpeg.P(">`") * lpeg.Cs(1) * lpeg.P('|')) / replace_04 - local stage_05 = (lpeg.P("<~") * lpeg.Cs(1) * lpeg.P('|')) / replace_05 - local stage_06 = (lpeg.P(">~") * lpeg.Cs(1) * lpeg.P('|')) / replace_06 - local stage_07 = (lpeg.P('"\'') * lpeg.Cs(1) ) / replace_07 - local stage_08 = (lpeg.P('"`') * lpeg.Cs(1) ) / replace_08 - local stage_09 = (lpeg.P('"~') * lpeg.Cs(1) ) / replace_09 - local stage_10 = (lpeg.P("<'") * lpeg.Cs(1) ) / replace_10 - local stage_11 = (lpeg.P(">'") * lpeg.Cs(1) ) / replace_11 - local stage_12 = (lpeg.P("<`") * lpeg.Cs(1) ) / replace_12 - local stage_13 = (lpeg.P(">`") * lpeg.Cs(1) ) / replace_13 - local stage_14 = (lpeg.P(">~") * lpeg.Cs(1) ) / replace_14 - local stage_15 = (lpeg.P(">~") * lpeg.Cs(1) ) / replace_15 - local stage_16 = (lpeg.P("'") * lpeg.Cs(1) * lpeg.P('|')) / replace_16 - local stage_17 = (lpeg.P("`") * lpeg.Cs(1) * lpeg.P('|')) / replace_17 - local stage_18 = (lpeg.P("~") * lpeg.Cs(1) * lpeg.P('|')) / replace_18 - local stage_19 = (lpeg.P("'") * lpeg.Cs(1) ) / replace_19 - local stage_20 = (lpeg.P("`") * lpeg.Cs(1) ) / replace_20 - local stage_21 = (lpeg.P("~") * lpeg.Cs(1) ) / replace_21 - local stage_22 = (lpeg.P("<") * lpeg.Cs(1) ) / replace_22 - local stage_23 = (lpeg.P(">") * lpeg.Cs(1) ) / replace_23 - local stage_24 = (lpeg.Cs(1) * lpeg.P('|') ) / replace_24 - local stage_25 = (lpeg.P('"') * lpeg.Cs(1) ) / replace_25 - local stage_26 = (lpeg.Cs(1) ) / replace_26 - - local stages = - skips_01 + skips_02 + - stage_01 + stage_02 + stage_03 + stage_04 + stage_05 + - stage_06 + stage_07 + stage_08 + stage_09 + stage_10 + - stage_11 + stage_12 + stage_13 + stage_14 + stage_15 + - stage_16 + stage_17 + stage_18 + stage_19 + stage_20 + - stage_21 + stage_22 + stage_23 + stage_24 + stage_25 + - stage_26 - - local parser = lpeg.Cs((stages + 1)^0) + local greek_01 = (lpeg.P("<'") * lpeg.Cs(1) * lpeg.P('|')) / replace_01 + local greek_02 = (lpeg.P(">'") * lpeg.Cs(1) * lpeg.P('|')) / replace_02 + local greek_03 = (lpeg.P("<`") * lpeg.Cs(1) * lpeg.P('|')) / replace_03 + local greek_04 = (lpeg.P(">`") * lpeg.Cs(1) * lpeg.P('|')) / replace_04 + local greek_05 = (lpeg.P("<~") * lpeg.Cs(1) * lpeg.P('|')) / replace_05 + local greek_06 = (lpeg.P(">~") * lpeg.Cs(1) * lpeg.P('|')) / replace_06 + local greek_07 = (lpeg.P('"\'') * lpeg.Cs(1) ) / replace_07 + local greek_08 = (lpeg.P('"`') * lpeg.Cs(1) ) / replace_08 + local greek_09 = (lpeg.P('"~') * lpeg.Cs(1) ) / replace_09 + local greek_10 = (lpeg.P("<'") * lpeg.Cs(1) ) / replace_10 + local greek_11 = (lpeg.P(">'") * lpeg.Cs(1) ) / replace_11 + local greek_12 = (lpeg.P("<`") * lpeg.Cs(1) ) / replace_12 + local greek_13 = (lpeg.P(">`") * lpeg.Cs(1) ) / replace_13 + local greek_14 = (lpeg.P("<~") * lpeg.Cs(1) ) / replace_14 + local greek_15 = (lpeg.P(">~") * lpeg.Cs(1) ) / replace_15 + local greek_16 = (lpeg.P("'") * lpeg.Cs(1) * lpeg.P('|')) / replace_16 + local greek_17 = (lpeg.P("`") * lpeg.Cs(1) * lpeg.P('|')) / replace_17 + local greek_18 = (lpeg.P("~") * lpeg.Cs(1) * lpeg.P('|')) / replace_18 + local greek_19 = (lpeg.P("'") * lpeg.Cs(1) ) / replace_19 + local greek_20 = (lpeg.P("`") * lpeg.Cs(1) ) / replace_20 + local greek_21 = (lpeg.P("~") * lpeg.Cs(1) ) / replace_21 + local greek_22 = (lpeg.P("<") * lpeg.Cs(1) ) / replace_22 + local greek_23 = (lpeg.P(">") * lpeg.Cs(1) ) / replace_23 + local greek_24 = (lpeg.Cs(1) * lpeg.P('|') ) / replace_24 + local greek_25 = (lpeg.P('"') * lpeg.Cs(1) ) / replace_25 + local greek_26 = (lpeg.Cs(1) ) / replace_26 + + local skips = + skips_01 + skips_02 + + local greek = + greek_01 + greek_02 + greek_03 + greek_04 + greek_05 + + greek_06 + greek_07 + greek_08 + greek_09 + greek_10 + + greek_11 + greek_12 + greek_13 + greek_14 + greek_15 + + greek_16 + greek_17 + greek_18 + greek_19 + greek_20 + + greek_21 + greek_22 + greek_23 + greek_24 + greek_25 + + greek_26 + + local spacing = lpeg.S(" \n\r\t") + local startgreek = lpeg.P("\\startgreek") + local stopgreek = lpeg.P("\\stopgreek") + local localgreek = lpeg.P("\\localgreek") + local lbrace = lpeg.P("{") + local rbrace = lpeg.P("}") + + local documentparser = lpeg.Cs((skips + greek + 1)^0) + + local contextgrammar = lpeg.Cs ( lpeg.P { "scan", + ["scan"] = (lpeg.V("global") + lpeg.V("local") + skips + 1)^0, + ["global"] = startgreek * ((skips + greek + 1)-stopgreek )^0 , + ["local"] = localgreek * lpeg.V("grouped"), + ["grouped"] = spacing^0 * lbrace * (lpeg.V("grouped") + skips + (greek - rbrace))^0 * rbrace, + } ) + + converters['greek'] = { + document = documentparser, + context = contextgrammar, + } -- lpeg.print(parser): 254 lines function scripts.babel.convert(filename) if filename and filename ~= empty then - local data = io.loaddata(filename) - if data then - data = parser:match(data) - io.savedata(filename .. ".utf", data) + local data = io.loaddata(filename) or "" + if data ~= "" then + local language = environment.argument("language") or "" + if language ~= "" then + local converter = converters[language] + if converter then + local structure = environment.argument("structure") or "document" + converter = converter[structure] + if converter then + input.report(string.format("converting '%s' using language '%s' with structure '%s'", filename, language, structure)) + data = converter:match(data) + local newfilename = filename .. ".utf" + io.savedata(newfilename, data) + input.report(string.format("converted data saved in '%s'", newfilename)) + else + input.report(string.format("unknown structure '%s' language '%s'", structure, language)) + end + else + input.report(string.format("no converter for language '%s'", language)) + end + else + input.report(string.format("provide language")) + end + else + input.report(string.format("no data in '%s'",filename)) end end end + --~ print(contextgrammar:match [[ + --~ oeps abg \localgreek{a} + --~ \startgreek abg \stopgreek \oeps + --~ oeps abg \localgreek{a{b}\oeps g} + --~ ]]) + end -banner = banner .. " | conversion tools " +banner = banner .. " | babel conversion tools " messages.help = [[ +--language=string conversion language (e.g. greek) +--structure=string obey given structure (e.g. 'document', default: 'context') --convert convert babel codes into utf ]] @@ -366,3 +427,4 @@ if environment.argument("convert") then else input.help(banner,messages.help) end + diff --git a/scripts/context/lua/mtx-cache.lua b/scripts/context/lua/mtx-cache.lua index 8bd3b7a79..0fdaca6a4 100644 --- a/scripts/context/lua/mtx-cache.lua +++ b/scripts/context/lua/mtx-cache.lua @@ -1,4 +1,10 @@ -dofile(input.find_file(instance,"luat-log.lua")) +if not modules then modules = { } end modules ['mtx-cache'] = { + version = 1.001, + comment = "companion to mtxrun.lua", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} texmf.instance = instance -- we need to get rid of this / maybe current instance in global table diff --git a/scripts/context/lua/mtx-chars.lua b/scripts/context/lua/mtx-chars.lua index 470846419..77c74cf51 100644 --- a/scripts/context/lua/mtx-chars.lua +++ b/scripts/context/lua/mtx-chars.lua @@ -1,4 +1,10 @@ -dofile(input.find_file(instance,"luat-log.lua")) +if not modules then modules = { } end modules ['mtx-chars'] = { + version = 1.001, + comment = "companion to mtxrun.lua", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} texmf.instance = instance -- we need to get rid of this / maybe current instance in global table diff --git a/scripts/context/lua/mtx-context.lua b/scripts/context/lua/mtx-context.lua index c444dfd1a..2e7855847 100644 --- a/scripts/context/lua/mtx-context.lua +++ b/scripts/context/lua/mtx-context.lua @@ -1,4 +1,10 @@ -dofile(input.find_file(instance,"luat-log.lua")) +if not modules then modules = { } end modules ['mtx-context'] = { + version = 1.001, + comment = "companion to mtxrun.lua", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} texmf.instance = instance -- we need to get rid of this / maybe current instance in global table @@ -468,9 +474,11 @@ function scripts.context.multipass.makeoptionfile(jobname,ctxdata) setvalues("usemodules" , "\\usemodule[%s]") setvalues("environments" , "\\environment %s ") -- ctx stuff - setvalues(ctxdata.modes, "\\enablemode[%s]") - setvalues(ctxdata.modules, "\\usemodule[%s]") - setvalues(ctxdata.environments, "\\environment %s ") + if ctxdata then + setvalues(ctxdata.modes, "\\enablemode[%s]") + setvalues(ctxdata.modules, "\\usemodule[%s]") + setvalues(ctxdata.environments, "\\environment %s ") + end -- done setalways( "\\protect") setalways( "\\endinput") @@ -497,11 +505,12 @@ function scripts.context.multipass.copytuifile(jobname) end function scripts.context.run(ctxdata) - -- todo: interface -for k,v in pairs(ctxdata.flags) do - environment.setargument(k,v) -end - + if ctxdata then + -- todo: interface + for k,v in pairs(ctxdata.flags) do + environment.setargument(k,v) + end + end local files = environment.files if #files > 0 then input.identify_cnf(instance) diff --git a/scripts/context/lua/mtx-convert.lua b/scripts/context/lua/mtx-convert.lua new file mode 100644 index 000000000..c9827c8b7 --- /dev/null +++ b/scripts/context/lua/mtx-convert.lua @@ -0,0 +1,86 @@ +if not modules then modules = { } end modules ['mtx-convert'] = { + version = 1.001, + comment = "companion to mtxrun.lua", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +do + + graphics = graphics or { } + graphics.converters = graphics.converters or { } + + local gsprogram = (os.platform == "windows" and "gswin32c") or "gs" + local gstemplate = "%s -q -sDEVICE=pdfwrite -dEPSCrop -dNOPAUSE -dNOCACHE -dBATCH -dAutoRotatePages=/None -dProcessColorModel=/DeviceCMYK -sOutputFile=%s %s -c quit" + + function graphics.converters.epstopdf(inputpath,outputpath,epsname) + inputpath = inputpath or "." + outputpath = outputpath or "." + local oldname = file.join(inputpath,epsname) + local newname = file.join(outputpath,file.replacesuffix(epsname,"pdf")) + local et = lfs.attributes(oldname,"modification") + local pt = lfs.attributes(newname,"modification") + if not pt or et > pt then + dir.mkdirs(outputpath) + local tmpname = file.replacesuffix(newname,"tmp") + local command = string.format(gstemplate,gsprogram,tmpname,oldname) + os.execute(command) + os.remove(newname) + os.rename(tmpname,newname) + end + end + + function graphics.converters.convertpath(inputpath,outputpath) + for name in lfs.dir(inputpath or ".") do + if name:find("%.$") then + -- skip . and .. + elseif name:find("%.eps$") then + graphics.converters.epstopdf(inputpath,outputpath, name) + elseif lfs.attributes(inputpath .. "/".. name,"mode") == "directory" then + graphics.converters.convertpath(inputpath .. "/".. name,outputpath .. "/".. name) + end + end + end + +end + +texmf.instance = instance -- we need to get rid of this / maybe current instance in global table + +scripts = scripts or { } +scripts.convert = scripts.convert or { } + +scripts.convert.delay = 5 * 60 -- 5 minutes + +function scripts.convert.convertall() + local watch = environment.arguments.watch or false + local delay = environment.arguments.delay or scripts.convert.delay + local input = environment.arguments.inputpath or "." + local output = environment.arguments.outputpath or "." + while true do + graphics.converters.convertpath(input, output) + if watch then + os.sleep(delay) + else + break + end + end +end + +banner = banner .. " | graphic conversion tools " + +messages.help = [[ +--convertall convert all graphics on path +--inputpath=string original graphics path +--outputpath=string converted graphics path +--watch watch folders +--delay time between sweeps +]] + +input.verbose = true + +if environment.argument("convertall") then + scripts.convert.convertall() +else + input.help(banner,messages.help) +end diff --git a/scripts/context/lua/mtx-fonts.lua b/scripts/context/lua/mtx-fonts.lua index ba5215ab1..395e9764e 100644 --- a/scripts/context/lua/mtx-fonts.lua +++ b/scripts/context/lua/mtx-fonts.lua @@ -1,4 +1,11 @@ -dofile(input.find_file(instance,"luat-log.lua")) +if not modules then modules = { } end modules ['mtx-fonts'] = { + version = 1.001, + comment = "companion to mtxrun.lua", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + dofile(input.find_file(instance,"font-syn.lua")) texmf.instance = instance -- we need to get rid of this / maybe current instance in global table diff --git a/scripts/context/lua/mtx-watch.lua b/scripts/context/lua/mtx-watch.lua new file mode 100644 index 000000000..651865ab4 --- /dev/null +++ b/scripts/context/lua/mtx-watch.lua @@ -0,0 +1,224 @@ +if not modules then modules = { } end modules ['mtx-watch'] = { + version = 1.001, + comment = "companion to mtxrun.lua", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +texmf.instance = instance -- we need to get rid of this / maybe current instance in global table + +scripts = scripts or { } +scripts.watch = scripts.watch or { } + +function scripts.watch.watch() + local delay = environment.argument("delay") or 5 + local logpath = environment.argument("logpath") or "" + local pipe = environment.argument("pipe") or false + if #environment.files > 0 then + for _, path in ipairs(environment.files) do + logs.report("watch", "watching path ".. path) + end + local function glob(files,path) + for name in lfs.dir(path) do + if name:find("^%.") then + -- skip . and .. + else + name = path .. "/" .. name + local a = lfs.attributes(name) + if not a then + -- weird + elseif a.mode == "directory" then + if name:find("graphics$") or name:find("figures$") or name:find("resources$") then + -- skip these too + else + glob(files,name) + end + elseif name:find(".%luj$") then + files[name] = a.change or a.ctime or a.modification or a.mtime + end + end + end + end + local n = 0 + local function process() + local done = false + for _, path in ipairs(environment.files) do + lfs.chdir(path) + local files = { } + glob(files,path) + table.sort(files) -- what gets sorted here + for name, time in pairs(files) do + --~ local ok, joblog = xpcall(function() return dofile(name) end, function() end ) + local ok, joblog = pcall(dofile,name) + if ok and joblog then + if joblog.status == "processing" then + logs.report("watch",string.format("aborted job, %s added to queue",name)) + joblog.status = "queued" + io.savedata(name, table.serialize(joblog,true)) + elseif joblog.status == "queued" then + local command = joblog.command + if command then + local replacements = { + inputpath = (joblog.paths and joblog.paths.input ) or ".", + outputpath = (joblog.paths and joblog.paths.output) or ".", + filename = joblog.filename or "", + } + command = command:gsub("%%(.-)%%", replacements) + if command ~= "" then + joblog.status = "processing" + joblog.runtime = os.time() -- os.clock() + io.savedata(name, table.serialize(joblog,true)) + logs.report("watch",string.format("running: %s", command)) + local newpath = file.dirname(name) + io.flush() + local result = "" + if newpath ~= "" and newpath ~= "." then + local oldpath = lfs.currentdir() + lfs.chdir(newpath) + if pipe then result = os.resultof(command) else result = os.execute(command) end + lfs.chdir(oldpath) + else + if pipe then result = os.resultof(command) else result = os.execute(command) end + end + logs.report("watch",string.format("return value: %s", result)) + done = true + local path, base = replacements.outputpath, file.basename(replacements.filename) + joblog.runtime = os.time() - joblog.runtime -- os.clock() - joblog.runtime + joblog.result = file.replacesuffix(file.join(path,base),"pdf") + joblog.size = lfs.attributes(joblog.result,"size") + joblog.status = "finished" + else + joblog.status = "invalid command" + end + else + joblog.status = "no command" + end + -- pcall, when error sleep + again + io.savedata(name, table.serialize(joblog,true)) + if logpath ~= "" then + local name = string.format("%s/%s%04i%09i.lua", logpath, os.time(), math.floor((os.clock()*100)%1000), math.random(99999999)) + io.savedata(name, table.serialize(joblog,true)) + logs.report("watch", "saving joblog ".. name) + end + end + end + end + end + end + local function wait() + io.flush() + if not done then + n = n + 1 + if n >= 10 then + logs.report("watch", "still sleeping " .. os.clock()) + n = 0 + end + os.sleep(delay) + end + end + while true do + pcall(process) + pcall(wait) + end + else + logs.report("watch", "no paths to watch") + end +end + +function scripts.watch.collect_logs(path) -- clean 'm up too + path = path or environment.argument("logpath") or "" + path = (path == "" and ".") or path + local files = dir.globfiles(path,false,"^%d+%.lua$") + local collection = { } + local valid = table.tohash({"filename","result","runtime","size","status"}) + for _, name in ipairs(files) do + local t = dofile(name) + if t and type(t) == "table" and t.status then + for k, v in pairs(t) do + if not valid[k] then + t[k] = nil + end + end + collection[name:gsub("[^%d]","")] = t + end + end + return collection +end + +function scripts.watch.save_logs(collection,path) -- play safe + if collection and not table.is_empty(collection) then + path = path or environment.argument("logpath") or "" + path = (path == "" and ".") or path + local filename = string.format("%s/collected-%s.lua",path,tostring(os.time())) + io.savedata(filename,table.serialize(collection,true)) + local check = dofile(filename) + for k,v in pairs(check) do + if not collection[k] then + logs.error("watch", "error in saving file") + os.remove(filename) + return false + end + end + for k,v in pairs(check) do + os.remove(string.format("%s.lua",k)) + end + return true + else + return false + end +end + +function scripts.watch.collect_collections(path) -- removes duplicates + path = path or environment.argument("logpath") or "" + path = (path == "" and ".") or path + local files = dir.globfiles(path,false,"^collected%-%d+%.lua$") + local collection = { } + for _, name in ipairs(files) do + local t = dofile(name) + if t and type(t) == "table" then + for k, v in pairs(t) do + collection[k] = v + end + end + end + return collection +end + +function scripts.watch.show_logs(path) -- removes duplicates + local collection = scripts.watch.collect_collections(path) or { } + local max = 0 + for k,v in pairs(collection) do + v = v.filename or "?" + if #v > max then max = #v end + end + print(max) + for k,v in ipairs(table.sortedkeys(collection)) do + local c = collection[v] + local f, s, r, n = c.filename or "?", c.status or "?", c.runtime or 0, c.size or 0 + logs.report("watch", string.format("%s %s %3i %8i %s",string.padd(f,max," "),string.padd(s,10," "),r,n,v)) + end +end + +banner = banner .. " | watchdog" + +messages.help = [[ +--logpath optional path for log files +--watch watch given path +--pipe use pipe instead of execute +--delay delay between sweeps +--collect condense log files +--showlog show log data +]] + +input.verbose = true + +if environment.argument("watch") then + scripts.watch.watch() +elseif environment.argument("collect") then + scripts.watch.save_logs(scripts.watch.collect_logs()) +elseif environment.argument("showlog") then + scripts.watch.show_logs() +else + input.help(banner,messages.help) +end diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua index baad28e84..d180fa9b9 100644 --- a/scripts/context/lua/mtxrun.lua +++ b/scripts/context/lua/mtxrun.lua @@ -1,5 +1,14 @@ #!/usr/bin/env texlua +if not modules then modules = { } end modules ['mtxrun'] = { + version = 1.001, + comment = "runner, lua replacement for texmfstart.rb", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + + -- one can make a stub: -- -- #!/bin/sh @@ -29,7 +38,7 @@ -- remember for subruns: _CTX_K_S_#{original}_ -- remember for subruns: TEXMFSTART.#{original} [tex.rb texmfstart.rb] -banner = "version 1.0.1 - 2007+ - PRAGMA ADE / CONTEXT" +banner = "version 1.0.2 - 2007+ - PRAGMA ADE / CONTEXT" texlua = true -- begin library merge @@ -370,6 +379,49 @@ function string:split_settings() -- no {} handling, see l-aux for lpeg variant end +-- filename : l-lpeg.lua +-- author : Hans Hagen, PRAGMA-ADE, Hasselt NL +-- copyright: PRAGMA ADE / ConTeXt Development Team +-- license : see context related readme files + +if not versions then versions = { } end versions['l-lpeg'] = 1.001 + +--~ l-lpeg.lua : + +--~ lpeg.digit = lpeg.R('09')^1 +--~ lpeg.sign = lpeg.S('+-')^1 +--~ lpeg.cardinal = lpeg.P(lpeg.sign^0 * lpeg.digit^1) +--~ lpeg.integer = lpeg.P(lpeg.sign^0 * lpeg.digit^1) +--~ lpeg.float = lpeg.P(lpeg.sign^0 * lpeg.digit^0 * lpeg.P('.') * lpeg.digit^1) +--~ lpeg.number = lpeg.float + lpeg.integer +--~ lpeg.oct = lpeg.P("0") * lpeg.R('07')^1 +--~ lpeg.hex = lpeg.P("0x") * (lpeg.R('09') + lpeg.R('AF'))^1 +--~ lpeg.uppercase = lpeg.P("AZ") +--~ lpeg.lowercase = lpeg.P("az") + +--~ lpeg.eol = lpeg.S('\r\n\f')^1 -- includes formfeed +--~ lpeg.space = lpeg.S(' ')^1 +--~ lpeg.nonspace = lpeg.P(1-lpeg.space)^1 +--~ lpeg.whitespace = lpeg.S(' \r\n\f\t')^1 +--~ lpeg.nonwhitespace = lpeg.P(1-lpeg.whitespace)^1 + +function lpeg.anywhere(pattern) --slightly adapted from website + return lpeg.P { lpeg.P(pattern) + 1 * lpeg.V(1) } +end + +function lpeg.startswith(pattern) --slightly adapted + return lpeg.P(pattern) +end + +--~ g = lpeg.splitter(" ",function(s) ... end) -- gmatch:lpeg = 3:2 + +function lpeg.splitter(pattern, action) + return (((1-lpeg.P(pattern))^1)/action+1)^0 +end + + + + -- filename : l-table.lua -- comment : split off from luat-lib -- author : Hans Hagen, PRAGMA-ADE, Hasselt NL @@ -443,6 +495,7 @@ function table.merge(t, ...) t[k] = v end end + return t end function table.merged(...) @@ -455,6 +508,25 @@ function table.merged(...) return tmp end +function table.imerge(t, ...) + for _, list in ipairs({...}) do + for k,v in ipairs(list) do + t[#t+1] = v + end + end + return t +end + +function table.imerged(...) + local tmp = { } + for _, list in ipairs({...}) do + for _,v in pairs(list) do + tmp[#tmp+1] = v + end + end + return tmp +end + if not table.fastcopy then function table.fastcopy(old) -- fast one @@ -1101,6 +1173,38 @@ do end +function io.ask(question,default,options) + while true do + io.write(question) + if options then + io.write(string.format(" [%s]",table.concat(options,"|"))) + end + if default then + io.write(string.format(" [%s]",default)) + end + io.write(string.format(" ")) + local answer = io.read() + answer = answer:gsub("^%s*(.*)%s*$","%1") + if answer == "" and default then + return default + elseif not options then + return answer + else + for _,v in pairs(options) do + if v == answer then + return answer + end + end + local pattern = "^" .. answer + for _,v in pairs(options) do + if v:find(pattern) then + return v + end + end + end + end +end + -- filename : l-md5.lua -- author : Hans Hagen, PRAGMA-ADE, Hasselt NL @@ -1117,7 +1221,7 @@ if md5 then do if not md5.HEX then function md5.HEX(str) return convert(str,"%02X") end end if not md5.hex then function md5.hex(str) return convert(str,"%02x") end end - if not md5.dec then function md5.dec(str) return convert(stt,"%03i") end end + if not md5.dec then function md5.dec(str) return convert(str,"%03i") end end end end @@ -1325,15 +1429,18 @@ dir = { } if lfs then function dir.glob_pattern(path,patt,recurse,action) - for name in lfs.dir(path) do - local full = path .. '/' .. name - local mode = lfs.attributes(full,'mode') - if mode == 'file' then - if name:find(patt) then - action(full) + local ok, scanner = xpcall(function() return lfs.dir(path) end, function() end) -- kepler safe + if ok and type(scanner) == "function" then + for name in scanner do + local full = path .. '/' .. name + local mode = lfs.attributes(full,'mode') + if mode == 'file' then + if name:find(patt) then + action(full) + end + elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then + dir.glob_pattern(full,patt,recurse,action) end - elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then - dir.glob_pattern(full,patt,recurse,action) end end end @@ -1358,6 +1465,30 @@ if lfs then return t end + function dir.globfiles(path,recurse,func,files) + if type(func) == "string" then + local s = func -- alas, we need this indirect way + func = function(name) return name:find(s) end + end + files = files or { } + for name in lfs.dir(path) do + if name:find("^%.") then + --- skip + elseif lfs.attributes(name,'mode') == "directory" then + if recurse then + dir.globfiles(path .. "/" .. name,recurse,func,files) + end + elseif func then + if func(name) then + files[#files+1] = path .. "/" .. name + end + else + files[#files+1] = path .. "/" .. name + end + end + return files + end + -- t = dir.glob("c:/data/develop/context/sources/**/????-*.tex") -- t = dir.glob("c:/data/develop/tex/texmf/**/*.tex") -- t = dir.glob("c:/data/develop/context/texmf/**/*.tex") @@ -1374,35 +1505,25 @@ if lfs then --~ mkdirs(".","/a/b/c") --~ mkdirs("a","b","c") - function dir.mkdirs(...) -- root,... or ... ; root is not split - local pth, err = "", false - for k,v in pairs({...}) do - if k == 1 then - if not lfs.isdir(v) then - -- print("no root path " .. v) - err = true - else - pth = v - end - elseif lfs.isdir(pth .. "/" .. v) then - pth = pth .. "/" .. v + function dir.mkdirs(...) + local pth, err, lst = "", false, table.concat({...},"/") + for _, s in ipairs(lst:split("/")) do + if pth == "" then + pth = (s == "" and "/") or s else - for _,s in pairs(v:split("/")) do - pth = pth .. "/" .. s - if not lfs.isdir(pth) then - ok = lfs.mkdir(pth) - if not lfs.isdir(pth) then - err = true - end - end - if err then break end - end + pth = pth .. "/" .. s + end + if s == "" then + -- can be network path + elseif not lfs.isdir(pth) then + lfs.mkdir(pth) end - if err then break end end return pth, not err end + dir.makedirs = dir.mkdirs + end @@ -1512,7 +1633,8 @@ xml.xmlns = { } do - local parser = lpeg.P(false) -- printing shows that this has no side effects + local check = lpeg.P(false) + local parse = check --[[ldx-- <p>The next function associates a namespace prefix with an <l n='url'/>. This @@ -1524,7 +1646,8 @@ do --ldx]]-- function xml.registerns(namespace, pattern) -- pattern can be an lpeg - parser = parser + lpeg.C(lpeg.P(pattern:lower())) / namespace + check = check + lpeg.C(lpeg.P(pattern:lower())) / namespace + parse = lpeg.P { lpeg.P(check) + 1 * lpeg.V(1) } end --[[ldx-- @@ -1538,7 +1661,7 @@ do --ldx]]-- function xml.checkns(namespace,url) - local ns = parser:match(url:lower()) + local ns = parse:match(url:lower()) if ns and namespace ~= ns then xml.xmlns[namespace] = ns end @@ -1556,7 +1679,7 @@ do --ldx]]-- function xml.resolvens(url) - return parser:match(url:lower()) or "" + return parse:match(url:lower()) or "" end --[[ldx-- @@ -1607,11 +1730,15 @@ do local mt = { __tostring = xml.text } + function xml.check_error(top,toclose) + return "" + end + local function add_attribute(namespace,tag,value) if tag == "xmlns" then xmlns[#xmlns+1] = xml.resolvens(value) at[tag] = value - elseif ns == "xmlns" then + elseif namespace == "xmlns" then xml.checkns(tag,value) at["xmlns:" .. tag] = value else @@ -1623,7 +1750,7 @@ do dt[#dt+1] = spacing end local resolved = (namespace == "" and xmlns[#xmlns]) or nsremap[namespace] or namespace - top = { ns=namespace or "", nr=resolved, tg=tag, at=at, dt={}, __p__ = stack[#stack] } + top = { ns=namespace or "", rn=resolved, tg=tag, at=at, dt={}, __p__ = stack[#stack] } setmetatable(top, mt) dt = top.dt stack[#stack+1] = top @@ -1636,9 +1763,9 @@ do local toclose = remove(stack) top = stack[#stack] if #stack < 1 then - errorstr = string.format("nothing to close with %s", tag) + errorstr = string.format("nothing to close with %s %s", tag, xml.check_error(top,toclose) or "") elseif toclose.tg ~= tag then -- no namespace check - errorstr = string.format("unable to close %s with %s", toclose.tg, tag) + errorstr = string.format("unable to close %s with %s %s", toclose.tg, tag, xml.check_error(top,toclose) or "") end dt = top.dt dt[#dt+1] = toclose @@ -1654,7 +1781,7 @@ do top = stack[#stack] setmetatable(top, mt) dt = top.dt - dt[#dt+1] = { ns=namespace or "", nr=resolved, tg=tag, at=at, dt={}, __p__ = top } + dt[#dt+1] = { ns=namespace or "", rn=resolved, tg=tag, at=at, dt={}, __p__ = top } at = { } if at.xmlns then remove(xmlns) @@ -1743,14 +1870,13 @@ do -- text + comment + emptyelement + cdata + instruction + lpeg.V("parent"), -- 5.8 -- text + lpeg.V("parent") + emptyelement + comment + cdata + instruction, -- 5.5 - local grammar = lpeg.P { "preamble", preamble = utfbom^0 * instruction^0 * (doctype + comment + instruction)^0 * lpeg.V("parent") * trailer, parent = beginelement * lpeg.V("children")^0 * endelement, children = text + lpeg.V("parent") + emptyelement + comment + cdata + instruction, } - function xml.convert(data, no_root) -- no collapse any more + function xml.convert(data, no_root) stack, top, at, xmlns, errorstr, result = {}, {}, {}, {}, nil, nil stack[#stack+1] = top top.dt = { } @@ -1761,7 +1887,7 @@ do errorstr = "invalid xml file" end if errorstr then - result = { dt = { { ns = "", tg = "error", dt = { errorstr }, at={} } } } + result = { dt = { { ns = "", tg = "error", dt = { errorstr }, at={}, er = true } }, error = true } setmetatable(stack, mt) if xml.error_handler then xml.error_handler("load",errorstr) end else @@ -1785,6 +1911,10 @@ do function. Maybe it will go away (when not used).</p> --ldx]]-- + function xml.is_valid(root) + return root and root.dt and root.dt[1] and type(root.dt[1]) == "table" and not root.dt[1].er + end + function xml.package(tag,attributes,data) local ns, tg = tag:match("^(.-):?([^:]+)$") local t = { ns = ns, tg = tg, dt = data or "", at = attributes or {} } @@ -1792,6 +1922,10 @@ do return t end + function xml.is_valid(root) + return root and not root.error + end + xml.error_handler = (logs and logs.report) or print end @@ -1804,16 +1938,18 @@ a filename or a file handle.</p> function xml.load(filename) if type(filename) == "string" then - local root, f = { }, io.open(filename,'r') + local f = io.open(filename,'r') if f then - root = xml.convert(f:read("*all")) + local root = xml.convert(f:read("*all")) f:close() + return root else - -- if we want an error: root = xml.convert("") + return xml.convert("") end - return root -- no nil but an empty table if it fails - else + elseif filename then -- filehandle return xml.convert(filename:read("*all")) + else + return xml.convert("") end end @@ -1955,10 +2091,10 @@ do else if ats then -- handle(format("<%s:%s %s/>",ens,etg,table.concat(ats," "))) - handle("<%" .. ens .. ":" .. etg .. table.concat(ats," ") .. "/>") + handle("<" .. ens .. ":" .. etg .. table.concat(ats," ") .. "/>") else -- handle(format("<%s:%s/>",ens,etg)) - handle("<%" .. ens .. ":" .. "/>") + handle("<" .. ens .. ":" .. "/>") end end else @@ -2159,9 +2295,20 @@ do [28] = "has value", [29] = "fast match", [30] = "select", + [31] = "expression", [40] = "processing instruction", } + local function make_expression(str) + str = str:gsub("@([a-zA-Z%-_]+)", "(a['%1'] or '')") + str = str:gsub("position%(%)", "i") + str = str:gsub("text%(%)", "t") + str = str:gsub("!=", "~=") + str = str:gsub("([^=!~<>])=([^=!~<>])", "%1==%2") + str = str:gsub("([a-zA-Z%-_]+)%(", "functions.%1(") + return str, loadstring(string.format("return function(functions,i,a,t) return %s end", str))() + end + local map = { } local space = lpeg.S(' \r\n\t') @@ -2182,7 +2329,7 @@ do local bar = lpeg.P('|') local hat = lpeg.P('^') local valid = lpeg.R('az', 'AZ', '09') + lpeg.S('_-') - local name_yes = lpeg.C(valid^1) * colon * lpeg.C(valid^1) + local name_yes = lpeg.C(valid^1) * colon * lpeg.C(valid^1 + star) -- permits ns:* local name_nop = lpeg.C(lpeg.P(true)) * lpeg.C(valid^1) local name = name_yes + name_nop local number = lpeg.C((lpeg.S('+-')^0 * lpeg.R('09')^1)) / tonumber @@ -2202,6 +2349,11 @@ do local is_value = lbracket * value * rbracket local is_number = lbracket * number * rbracket + local nobracket = 1-(lbracket+rbracket) -- must be improved + local is_expression = lbracket * lpeg.C(((lpeg.C(nobracket^1))/make_expression)) * rbracket + + local is_expression = lbracket * (lpeg.C(nobracket^1))/make_expression * rbracket + local is_one = name local is_none = exclam * name local is_one_of = ((lparent * names * rparent) + morenames) @@ -2237,6 +2389,9 @@ do local position = (is_one * is_number ) / function(...) map[#map+1] = { 30, true, ... } end local dont_position = (is_none * is_number ) / function(...) map[#map+1] = { 30, false, ... } end + local expression = (is_one * is_expression)/ function(...) map[#map+1] = { 31, true, ... } end + local dont_expression = (is_none * is_expression)/ function(...) map[#map+1] = { 31, false, ... } end + local instruction = (instructiontag * text ) / function(...) map[#map+1] = { 40, ... } end local nothing = (empty ) / function( ) map[#map+1] = { 15 } end -- 15 ? local crap = (1-slash)^1 @@ -2261,6 +2416,7 @@ do match_one_of_and_eq + match_one_of_and_ne + dont_match_and_eq + dont_match_and_ne + match_and_eq + match_and_ne + + dont_expression + expression + has_attribute + has_value + dont_match_one_of + match_one_of + dont_match + match + @@ -2294,8 +2450,10 @@ do -- root return false end - elseif #map == 2 and m == 12 and map[2][1] == 20 then - return { { 29, map[2][2], map[2][3] } } + elseif #map == 2 and m == 12 and map[2][1] == 20 then + -- return { { 29, map[2][2], map[2][3], map[2][4], map[2][5] } } + map[2][1] = 29 + return { map[2] } end if m ~= 11 and m ~= 12 and m ~= 13 and m ~= 14 and m ~= 15 and m ~= 16 then table.insert(map, 1, { 16 }) @@ -2355,6 +2513,20 @@ do end end + function xml.xshow(e,...) -- also handy when report is given, use () to isolate first e + local t = { ... } + local report = (type(t[#t]) == "function" and t[#t]) or fallbackreport + if not e then + report("<!-- no element -->\n") + elseif e.tg then + report(tostring(e) .. "\n") + else + for i=1,#e do + report(tostring(e[i]) .. "\n") + end + end + end + end --[[ldx-- @@ -2372,8 +2544,22 @@ advance what we want to do with the found element the handle gets three argument functions.</p> --ldx]]-- +xml.functions = { } + do + local functions = xml.functions + + functions.contains = string.find + functions.find = string.find + functions.upper = string.upper + functions.lower = string.lower + functions.number = tonumber + functions.boolean = toboolean + functions.oneof = function(s,...) -- slow + local t = {...} for i=1,#t do if s == t[i] then return true end end return false + end + function xml.traverse(root,pattern,handle,reverse,index,parent,wildcard) if not root then -- error return false @@ -2402,8 +2588,10 @@ do local rootdt = root.dt for k=1,#rootdt do local e = rootdt[k] - local ns, tg = e.rn or e.ns, e.tg - if ns == action[2] and tg == action[3] then + local ns, tg = (e.rn or e.ns), e.tg + local matched = ns == action[3] and tg == action[4] + if not action[2] then matched = not matched end + if matched then if handle(root,rootdt,k) then return false end end end @@ -2416,7 +2604,8 @@ do end else if (command == 16 or command == 12) and index == 1 then -- initial - wildcard = true +--~ wildcard = true + wildcard = command == 16 -- ok? index = index + 1 action = pattern[index] command = action and action[1] or 0 -- something is wrong @@ -2440,13 +2629,16 @@ do elseif reverse and index == #pattern then start, stop, step = stop, start, -1 end + local idx = 0 for k=start,stop,step do local e = rootdt[k] local ns, tg = e.rn or e.ns, e.tg if tg then + idx = idx + 1 if command == 30 then - local matched = ns == action[3] and tg == action[4] - if action[2] then matched = not matched end + local tg_a = action[4] + if tg == tg_a then matched = ns == action[3] elseif tg_a == '*' then matched, multiple = ns == action[3], true else matched = false end + if not action[2] then matched = not matched end if matched then n = n + dn if n == action[5] then @@ -2463,46 +2655,58 @@ do else local matched, multiple = false, false if command == 20 then -- match - matched = ns == action[2] and tg == action[3] - if action[2] then matched = not matched end + local tg_a = action[4] + if tg == tg_a then matched = ns == action[3] elseif tg_a == '*' then matched, multiple = ns == action[3], true else matched = false end + if not action[2] then matched = not matched end elseif command == 21 then -- match one of multiple = true - for i=2,#action,2 do + for i=3,#action,2 do if ns == action[i] and tg == action[i+1] then matched = true break end end - if action[2] then matched = not matched end + if not action[2] then matched = not matched end elseif command == 22 then -- eq - matched = ns == action[3] and tg == action[4] - if action[2] then matched = not matched end + local tg_a = action[4] + if tg == tg_a then matched = ns == action[3] elseif tg_a == '*' then matched, multiple = ns == action[3], true else matched = false end + if not action[2] then matched = not matched end matched = matched and e.at[action[6]] == action[7] elseif command == 23 then -- ne - matched = ns == action[3] and tg == action[4] - if action[2] then matched = not matched end + local tg_a = action[4] + if tg == tg_a then matched = ns == action[3] elseif tg_a == '*' then matched, multiple = ns == action[3], true else matched = false end + if not action[2] then matched = not matched end matched = mached and e.at[action[6]] ~= action[7] elseif command == 24 then -- one of eq multiple = true for i=3,#action-2,2 do if ns == action[i] and tg == action[i+1] then matched = true break end end - if action[2] then matched = not matched end + if not action[2] then matched = not matched end matched = matched and e.at[action[#action-1]] == action[#action] elseif command == 25 then -- one of ne multiple = true for i=3,#action-2,2 do if ns == action[i] and tg == action[i+1] then matched = true break end end - if action[2] then matched = not matched end + if not action[2] then matched = not matched end matched = matched and e.at[action[#action-1]] ~= action[#action] elseif command == 27 then -- has attribute - local ans = action[3] - matched = ns == action[3] and tg == action[4] - if action[2] then matched = not matched end + local tg_a = action[4] + if tg == tg_a then matched = ns == action[3] elseif tg_a == '*' then matched, multiple = ns == action[3], true else matched = false end + if not action[2] then matched = not matched end matched = matched and e.at[action[5]] elseif command == 28 then -- has value local edt = e.dt - matched = ns == action[3] and tg == action[4] - if action[2] then matched = not matched end + local tg_a = action[4] + if tg == tg_a then matched = ns == action[3] elseif tg_a == '*' then matched, multiple = ns == action[3], true else matched = false end + if not action[2] then matched = not matched end matched = matched and edt and edt[1] == action[5] + elseif command == 31 then + local edt = e.dt + local tg_a = action[4] + if tg == tg_a then matched = ns == action[3] elseif tg_a == '*' then matched, multiple = ns == action[3], true else matched = false end + if not action[2] then matched = not matched end + if matched then + matched = action[6](functions,idx,e.at,edt[1]) + end end if matched then -- combine tg test and at test if index == #pattern then @@ -2943,28 +3147,33 @@ do end end - function xml.include(xmldata,element,attribute,pathlist,collapse) - element = element or 'ctx:include' - attribute = attribute or 'name' - pathlist = pathlist or { '.' } - -- todo, check op ri + function xml.include(xmldata,pattern,attribute,recursive,findfile) + -- parse="text" (default: xml), encoding="" (todo) + pattern = pattern or 'include' + attribute = attribute or 'href' local function include(r,d,k) - local ek = d[k] - local name = (ek.at and ek.at[attribute]) or "" - if name ~= "" then - -- maybe file lookup in tree - local fullname - for _, path in ipairs(pathlist) do - if path == '.' then - fullname = name - else - fullname = file.join(path,name) - end - local f = io.open(fullname) + local ek, name = d[k], nil + if ek.at then + for a in attribute:gmatch("([^|]+)") do + name = ek.at[a] + if name then break end + end + end + if name then + name = (findfile and findfile(name)) or name + if name ~= "" then + local f = io.open(name) if f then - xml.assign(d,k,xml.load(f,collapse)) + if ek.at["parse"] == "text" then -- for the moment hard coded + d[k] = xml.escaped(f:read("*all")) + else + local xi = xml.load(f) + if recursive then + xml.include(xi,pattern,attribute,recursive,findfile) + end + xml.assign(d,k,xi) + end f:close() - break else xml.empty(d,k) end @@ -2973,7 +3182,7 @@ do xml.empty(d,k) end end - while xml.each_element(xmldata, element, include) do end + xml.each_element(xmldata, pattern, include) end function xml.strip_whitespace(root, pattern) @@ -3041,6 +3250,20 @@ do end) end + function xml.filters.found(root,pattern,check_content) + local found = false + traverse(root, lpath(pattern), function(r,d,k) + if check_content then + local dk = d and d[k] + found = dk and dk.dt and next(dk.dt) and true + else + found = true + end + return true + end) + return found + end + end --[[ldx-- @@ -3054,6 +3277,7 @@ xml.index = xml.filters.index xml.position = xml.filters.index xml.first = xml.filters.first xml.last = xml.filters.last +xml.found = xml.filters.found xml.each = xml.each_element xml.process = xml.process_element @@ -3102,12 +3326,46 @@ function xml.serialize_path(root,lpath,handle) xml.serialize(dk,handle) end -xml.escapes = { ['&'] = '&', ['<'] = '<', ['>'] = '>', ['"'] = '"' } -xml.unescapes = { } for k,v in pairs(xml.escapes) do xml.unescapes[v] = k end +--~ xml.escapes = { ['&'] = '&', ['<'] = '<', ['>'] = '>', ['"'] = '"' } +--~ xml.unescapes = { } for k,v in pairs(xml.escapes) do xml.unescapes[v] = k end -function xml.escaped (str) return str:gsub("(.)" , xml.escapes ) end -function xml.unescaped(str) return str:gsub("(&.-;)", xml.unescapes) end -function xml.cleansed (str) return str:gsub("<.->" , '' ) end -- "%b<>" +--~ function xml.escaped (str) return str:gsub("(.)" , xml.escapes ) end +--~ function xml.unescaped(str) return str:gsub("(&.-;)", xml.unescapes) end +--~ function xml.cleansed (str) return str:gsub("<.->" , '' ) end -- "%b<>" + +do + + -- 100 * 2500 * "oeps< oeps> oeps&" : gsub:lpeg|lpeg|lpeg + -- + -- 1021:0335:0287:0247 + + -- 10 * 1000 * "oeps< oeps> oeps& asfjhalskfjh alskfjh alskfjh alskfjh ;al J;LSFDJ" + -- + -- 1559:0257:0288:0190 (last one suggested by roberto) + + -- escaped = lpeg.Cs((lpeg.S("<&>") / xml.escapes + 1)^0) + -- escaped = lpeg.Cs((lpeg.S("<")/"<" + lpeg.S(">")/">" + lpeg.S("&")/"&" + 1)^0) + local normal = (1 - lpeg.S("<&>"))^0 + local special = lpeg.P("<")/"<" + lpeg.P(">")/">" + lpeg.P("&")/"&" + local escaped = lpeg.Cs(normal * (special * normal)^0) + + -- 100 * 1000 * "oeps< oeps> oeps&" : gsub:lpeg == 0153:0280:0151:0080 (last one by roberto) + + -- unescaped = lpeg.Cs((lpeg.S("<")/"<" + lpeg.S(">")/">" + lpeg.S("&")/"&" + 1)^0) + -- unescaped = lpeg.Cs((((lpeg.P("&")/"") * (lpeg.P("lt")/"<" + lpeg.P("gt")/">" + lpeg.P("amp")/"&") * (lpeg.P(";")/"")) + 1)^0) + local normal = (1 - lpeg.S"&")^0 + local special = lpeg.P("<")/"<" + lpeg.P(">")/">" + lpeg.P("&")/"&" + local unescaped = lpeg.Cs(normal * (special * normal)^0) + + -- 100 * 5000 * "oeps <oeps bla='oeps' foo='bar'> oeps </oeps> oeps " : gsub:lpeg == 623:501 msec (short tags, less difference) + + local cleansed = lpeg.Cs(((lpeg.P("<") * (1-lpeg.P(">"))^0 * lpeg.P(">"))/"" + 1)^0) + + function xml.escaped (str) return escaped :match(str) end + function xml.unescaped(str) return unescaped:match(str) end + function xml.cleansed (str) return cleansed :match(str) end + +end function xml.join(t,separator,lastseparator) if #t > 0 then @@ -3193,6 +3451,33 @@ end end --~ xml.lshow("/../../../a/!(b|c)[@d='e']/f") --~ xml.lshow("/../../../a/!b[@d!='e']/f") +--~ x = xml.convert([[ +--~ <a> +--~ <b n='01'>01</b> +--~ <b n='02'>02</b> +--~ <b n='03'>03</b> +--~ <b n='04'>OK</b> +--~ <b n='05'>05</b> +--~ <b n='06'>06</b> +--~ <b n='07'>ALSO OK</b> +--~ </a> +--~ ]]) + +--~ xml.trace_lpath = true + +--~ xml.xshow(xml.first(x,"b[position() > 2 and position() < 5 and text() == 'ok']")) +--~ xml.xshow(xml.first(x,"b[position() > 2 and position() < 5 and text() == upper('ok')]")) +--~ xml.xshow(xml.first(x,"b[@n=='03' or @n=='08']")) +--~ xml.xshow(xml.all (x,"b[number(@n)>2 and number(@n)<6]")) +--~ xml.xshow(xml.first(x,"b[find(text(),'ALSO')]")) + +--~ str = [[ +--~ <?xml version="1.0" encoding="utf-8"?> +--~ <story line='mojca'> +--~ <windows>my secret</mouse> +--~ </story> +--~ ]] + -- filename : l-utils.lua -- comment : split off from luat-lib @@ -3500,6 +3785,7 @@ end -- Beware, loading and saving is overloaded in luat-tmp! -- todo: instances.[hashes,cnffiles,configurations,522] -> ipairs (alles check, sneller) +-- todo: check escaping in find etc, too much, too slow if not versions then versions = { } end versions['luat-inp'] = 1.001 if not environment then environment = { } end @@ -3737,7 +4023,7 @@ input.settrace(tonumber(os.getenv("MTX.INPUT.TRACE") or os.getenv("MTX_INPUT_TRA -- These functions can be used to test the performance, especially -- loading the database files. -function input.start_timing(instance) +function input.starttiming(instance) if instance then instance.starttime = os.clock() if not instance.loadtime then @@ -3746,7 +4032,7 @@ function input.start_timing(instance) end end -function input.stop_timing(instance, report) +function input.stoptiming(instance, report) if instance and instance.starttime then instance.stoptime = os.clock() local loadtime = instance.stoptime - instance.starttime @@ -3760,9 +4046,6 @@ function input.stop_timing(instance, report) end end -input.stoptiming = input.stop_timing -input.starttiming = input.start_timing - function input.elapsedtime(instance) return string.format("%0.3f",instance.loadtime or 0) end @@ -4075,99 +4358,106 @@ function input.generatedatabase(instance,specification) return input.methodhandler('generators', instance, specification) end -function input.generators.tex(instance,specification) - local tag = specification - if not instance.lsrmode and lfs and lfs.dir then - input.report("scanning path",specification) - instance.files[tag] = { } - local files = instance.files[tag] - local n, m, r = 0, 0, 0 - local spec = specification .. '/' - local attributes = lfs.attributes - local directory = lfs.dir - local small = instance.smallcache - local function action(path) - local mode, full - if path then - full = spec .. path .. '/' - else - full = spec - end - for name in directory(full) do - if name:find("^%.") then - -- skip - elseif name:find("[%~%`%!%#%$%%%^%&%*%(%)%=%{%}%[%]%:%;\"\'%|%|%<%>%,%?\n\r\t]") then - -- texio.write_nl("skipping " .. name) - -- skip +do + + local weird = lpeg.anywhere(lpeg.S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t")) + + function input.generators.tex(instance,specification) + local tag = specification + if not instance.lsrmode and lfs and lfs.dir then + input.report("scanning path",specification) + instance.files[tag] = { } + local files = instance.files[tag] + local n, m, r = 0, 0, 0 + local spec = specification .. '/' + local attributes = lfs.attributes + local directory = lfs.dir + local small = instance.smallcache + local function action(path) + local mode, full + if path then + full = spec .. path .. '/' else - mode = attributes(full..name,'mode') - if mode == "directory" then - m = m + 1 - if path then - action(path..'/'..name) - else - action(name) - end - elseif path and mode == 'file' then - n = n + 1 - local f = files[name] - if f then - if not small then - if type(f) == 'string' then - files[name] = { f, path } - else - f[#f+1] = path - end + full = spec + end + for name in directory(full) do + if name:find("^%.") then + -- skip + -- elseif name:find("[%~%`%!%#%$%%%^%&%*%(%)%=%{%}%[%]%:%;\"\'%|%<%>%,%?\n\r\t]") then -- too much escaped + elseif weird:match(name) then + -- texio.write_nl("skipping " .. name) + -- skip + else + mode = attributes(full..name,'mode') + if mode == "directory" then + m = m + 1 + if path then + action(path..'/'..name) + else + action(name) end - else - files[name] = path - local lower = name:lower() - if name ~= lower then - files["remap:"..lower] = name - r = r + 1 + elseif path and mode == 'file' then + n = n + 1 + local f = files[name] + if f then + if not small then + if type(f) == 'string' then + files[name] = { f, path } + else + f[#f+1] = path + end + end + else + files[name] = path + local lower = name:lower() + if name ~= lower then + files["remap:"..lower] = name + r = r + 1 + end end end end end end - end - action() - input.report(string.format("%s files found on %s directories with %s uppercase remappings",n,m,r)) - else - local fullname = file.join(specification,input.lsrname) - local path = '.' - local f = io.open(fullname) - if f then - instance.files[tag] = { } - local files = instance.files[tag] - local small = instance.smallcache - input.report("loading lsr file",fullname) - -- for line in f:lines() do -- much slower then the next one - for line in (f:read("*a")):gmatch("(.-)\n") do - if line:find("^[%a%d]") then - local fl = files[line] - if fl then - if not small then - if type(fl) == 'string' then - files[line] = { fl, path } -- table - else - fl[#fl+1] = path + action() + input.report(string.format("%s files found on %s directories with %s uppercase remappings",n,m,r)) + else + local fullname = file.join(specification,input.lsrname) + local path = '.' + local f = io.open(fullname) + if f then + instance.files[tag] = { } + local files = instance.files[tag] + local small = instance.smallcache + input.report("loading lsr file",fullname) + -- for line in f:lines() do -- much slower then the next one + for line in (f:read("*a")):gmatch("(.-)\n") do + if line:find("^[%a%d]") then + local fl = files[line] + if fl then + if not small then + if type(fl) == 'string' then + files[line] = { fl, path } -- table + else + fl[#fl+1] = path + end + end + else + files[line] = path -- string + local lower = line:lower() + if line ~= lower then + files["remap:"..lower] = line end end else - files[line] = path -- string - local lower = line:lower() - if line ~= lower then - files["remap:"..lower] = line - end + path = line:match("%.%/(.-)%:$") or path -- match could be nil due to empty line end - else - path = line:match("%.%/(.-)%:$") or path -- match could be nil due to empty line end + f:close() end - f:close() end end + end -- savers, todo @@ -4590,10 +4880,168 @@ end -- a,b,c/{p,q,r}/d/{x,y,z}// -- a,b,c/{p,q/{x,y,z},r},d/{p,q,r} -- a,b,c/{p,q/{x,y,z},r},d/{p,q,r} +-- a{b,c}{d,e}f +-- {a,b,c,d} +-- {a,b,c/{p,q,r},d} +-- {a,b,c/{p,q,r}/d/{x,y,z}//} +-- {a,b,c/{p,q/{x,y,z}},d/{p,q,r}} +-- {a,b,c/{p,q/{x,y,z},w}v,d/{p,q,r}} + +-- this one is better and faster, but it took me a while to realize +-- that this kind of replacement is cleaner than messy parsing and +-- fuzzy concatenating we can probably gain a bit with selectively +-- applying lpeg, but experiments with lpeg parsing this proved not to +-- work that well; the parsing is ok, but dealing with the resulting +-- table is a pain because we need to work inside-out recursively + +--~ function input.aux.splitpathexpr(str, t, validate) +--~ -- no need for optimization, only called a few times, we can use lpeg for the sub +--~ t = t or { } +--~ while true do +--~ local done = false +--~ while true do +--~ ok = false +--~ str = str:gsub("([^{},]+){([^{}]-)}", function(a,b) +--~ local t = { } +--~ for s in b:gmatch("([^,]+)") do +--~ t[#t+1] = a .. s +--~ end +--~ ok, done = true, true +--~ return "{" .. table.concat(t,",") .. "}" +--~ end) +--~ if not ok then break end +--~ end +--~ while true do +--~ ok = false +--~ str = str:gsub("{([^{}]-)}([^{},]+)", function(a,b) +--~ local t = { } +--~ for s in a:gmatch("([^,]+)") do +--~ t[#t+1] = s .. b +--~ end +--~ ok, done = true, true +--~ return "{" .. table.concat(t,",") .. "}" +--~ end) +--~ if not ok then break end +--~ end +--~ while true do +--~ ok = false +--~ str = str:gsub("([,{]){([^{}]+)}([,}])", function(a,b,c) +--~ ok, done = true, true +--~ return a .. b .. c +--~ end) +--~ if not ok then break end +--~ end +--~ if not done then break end +--~ end +--~ while true do +--~ ok = false +--~ str = str:gsub("{([^{}]-)}{([^{}]-)}", function(a,b) +--~ local t = { } +--~ for sa in a:gmatch("([^,]+)") do +--~ for sb in b:gmatch("([^,]+)") do +--~ t[#t+1] = sa .. sb +--~ end +--~ end +--~ ok = true +--~ return "{" .. table.concat(t,",") .. "}" +--~ end) +--~ if not ok then break end +--~ end +--~ while true do +--~ ok = false +--~ str = str:gsub("{([^{}]-)}", function(a) +--~ ok = true +--~ return a +--~ end) +--~ if not ok then break end +--~ end +--~ if validate then +--~ for s in str:gmatch("([^,]+)") do +--~ s = validate(s) +--~ if s then t[#t+1] = s end +--~ end +--~ else +--~ for s in str:gmatch("([^,]+)") do +--~ t[#t+1] = s +--~ end +--~ end +--~ return t +--~ end + +function input.aux.splitpathexpr(str, t, validate) + -- no need for optimization, only called a few times, we can use lpeg for the sub + t = t or { } + local concat = table.concat + while true do + local done = false + while true do + ok = false + str = str:gsub("([^{},]+){([^{}]-)}", function(a,b) + local t = { } + b:piecewise(",", function(s) t[#t+1] = a .. s end) + ok, done = true, true + return "{" .. concat(t,",") .. "}" + end) + if not ok then break end + end + while true do + ok = false + str = str:gsub("{([^{}]-)}([^{},]+)", function(a,b) + local t = { } + a:piecewise(",", function(s) t[#t+1] = s .. b end) + ok, done = true, true + return "{" .. concat(t,",") .. "}" + end) + if not ok then break end + end + while true do + ok = false + str = str:gsub("([,{]){([^{}]+)}([,}])", function(a,b,c) + ok, done = true, true + return a .. b .. c + end) + if not ok then break end + end + if not done then break end + end + while true do + ok = false + str = str:gsub("{([^{}]-)}{([^{}]-)}", function(a,b) + local t = { } + a:piecewise(",", function(sa) + b:piecewise(",", function(sb) + t[#t+1] = sa .. sb + end) + end) + ok = true + return "{" .. concat(t,",") .. "}" + end) + if not ok then break end + end + while true do + ok = false + str = str:gsub("{([^{}]-)}", function(a) + ok = true + return a + end) + if not ok then break end + end + if validate then + str:piecewise(",", function(s) + s = validate(s) + if s then t[#t+1] = s end + end) + else + str:piecewise(",", function(s) + t[#t+1] = s + end) + end + return t +end function input.aux.expanded_path(instance,pathlist) -- a previous version fed back into pathlist - local i, n, oldlist, newlist, ok = 0, 0, { }, { }, false + local newlist, ok = { }, false for _,v in ipairs(pathlist) do if v:find("[{}]") then ok = true @@ -4601,45 +5049,11 @@ function input.aux.expanded_path(instance,pathlist) end end if ok then - for _,v in ipairs(pathlist) do - oldlist[#oldlist+1] = (v:gsub("([\{\}])", function(p) - if p == "{" then - i = i + 1 - if i > n then n = i end - return "<" .. (i-1) .. ">" - else - i = i - 1 - return "</" .. i .. ">" - end - end)) - end - for i=1,n do - while true do - local more = false - local pattern = "^(.-)<"..(n-i)..">(.-)</"..(n-i)..">(.-)$" - local t = { } - for _,v in ipairs(oldlist) do - local pre, mid, post = v:match(pattern) - if pre and mid and post then - more = true - for vv in string.gmatch(mid..',',"(.-),") do - if vv == '.' then - t[#t+1] = pre..post - else - t[#t+1] = pre..vv..post - end - end - else - t[#t+1] = v - end - end - oldlist = t - if not more then break end - end - end - for _,v in ipairs(oldlist) do - v = file.collapse_path(v) - if v ~= "" and not v:find(instance.dummy_path_expr) then newlist[#newlist+1] = v end + for _, v in ipairs(pathlist) do + input.aux.splitpathexpr(v, newlist, function(s) + s = file.collapse_path(s) + return s ~= "" and not s:find(instance.dummy_path_expr) and s + end) end else for _,v in ipairs(pathlist) do @@ -4652,6 +5066,83 @@ function input.aux.expanded_path(instance,pathlist) return newlist end +--~ old one, imperfect and not that efficient +--~ +--~ function input.aux.expanded_path(instance,pathlist) +--~ -- a previous version fed back into pathlist +--~ local i, n, oldlist, newlist, ok = 0, 0, { }, { }, false +--~ for _,v in ipairs(pathlist) do +--~ if v:find("[{}]") then +--~ ok = true +--~ break +--~ end +--~ end +--~ if ok then +--~ for _,v in ipairs(pathlist) do +--~ oldlist[#oldlist+1] = (v:gsub("([\{\}])", function(p) +--~ if p == "{" then +--~ i = i + 1 +--~ if i > n then n = i end +--~ return "<" .. (i-1) .. ">" +--~ else +--~ i = i - 1 +--~ return "</" .. i .. ">" +--~ end +--~ end)) +--~ end +--~ for i=1,n do +--~ while true do +--~ local more = false +--~ local pattern = "^(.-)<"..(n-i)..">(.-)</"..(n-i)..">(.-)$" +--~ local t = { } +--~ for _,v in ipairs(oldlist) do +--~ local pre, mid, post = v:match(pattern) +--~ if pre and mid and post then +--~ more = true +--~ for vv in string.gmatch(mid..',',"(.-),") do -- (mid, "([^,]+)") +--~ if vv == '.' then +--~ t[#t+1] = pre..post +--~ else +--~ t[#t+1] = pre..vv..post +--~ end +--~ end +--~ else +--~ t[#t+1] = v +--~ end +--~ end +--~ oldlist = t +--~ if not more then break end +--~ end +--~ end +--~ if true then +--~ -- many dups are possible due to messy resolve / order can be messed up too, brr ! +--~ local ok = { } +--~ for _,o in ipairs(oldlist) do +--~ for v in o:gmatch("([^,]+)") do +--~ if not ok[v] then +--~ ok[v] = true +--~ v = file.collapse_path(v) +--~ if v ~= "" and not v:find(instance.dummy_path_expr) then newlist[#newlist+1] = v end +--~ end +--~ end +--~ end +--~ else +--~ for _,v in ipairs(oldlist) do +--~ v = file.collapse_path(v) +--~ if v ~= "" and not v:find(instance.dummy_path_expr) then newlist[#newlist+1] = v end +--~ end +--~ end +--~ else +--~ for _,v in ipairs(pathlist) do +--~ for vv in string.gmatch(v..',',"(.-),") do +--~ vv = file.collapse_path(v) +--~ if vv ~= "" then newlist[#newlist+1] = vv end +--~ end +--~ end +--~ end +--~ return newlist +--~ end + --~ function input.is_readable(name) -- brrr, get rid of this --~ return name:find("^zip##") or file.is_readable(name) --~ end @@ -4750,24 +5241,51 @@ function input.suffixes_of_format(str) end end -function input.aux.qualified_path(filename) -- make platform dependent / not good yet - return - filename:find("^%.+/") or - filename:find("^/") or - filename:find("^%a+%:") or - filename:find("^%a+##") -end +--~ function input.aux.qualified_path(filename) -- make platform dependent / not good yet +--~ return +--~ filename:find("^%.+/") or +--~ filename:find("^/") or +--~ filename:find("^%a+%:") or +--~ filename:find("^%a+##") +--~ end + +--~ function input.normalize_name(original) +--~ -- internally we use type##spec##subspec ; this hackery slightly slows down searching +--~ local str = original or "" +--~ str = str:gsub("::", "##") -- :: -> ## +--~ str = str:gsub("^(%a+)://" ,"%1##") -- zip:// -> zip## +--~ str = str:gsub("(.+)##(.+)##/(.+)","%1##%2##%3") -- ##/spec -> ##spec +--~ if (input.trace>1) and (original ~= str) then +--~ input.logger('= normalizer',original.." -> "..str) +--~ end +--~ return str +--~ end + +do -- called about 700 times for an empty doc (font initializations etc) + -- i need to weed the font files for redundant calls + + local letter = lpeg.R("az","AZ") + local separator = lpeg.P("##") + + local qualified = lpeg.P(".")^0 * lpeg.P("/") + letter*lpeg.P(":") + letter^1*separator + local normalized = lpeg.Cs( + (letter^1*(lpeg.P("://")/"##") * (1-lpeg.P(false))^1) + + (lpeg.P("::")/"##" + (1-separator)^1*separator*(1-separator)^1*separator*(lpeg.P("/")/"") + 1)^0 + ) + + -- ./name ../name /name c: zip## (todo: use url internally and get rid of ##) + function input.aux.qualified_path(filename) + return qualified:match(filename) + end -function input.normalize_name(original) - -- internally we use type##spec##subspec ; this hackery slightly slows down searching - local str = original or "" - str = str:gsub("::", "##") -- :: -> ## - str = str:gsub("^(%a+)://" ,"%1##") -- zip:// -> zip## - str = str:gsub("(.+)##(.+)##/(.+)","%1##%2##%3") -- ##/spec -> ##spec - if (input.trace>1) and (original ~= str) then - input.logger('= normalizer',original.." -> "..str) + -- zip:// -> zip## ; :: -> ## ; aa##bb##/cc -> aa##bb##cc + function input.normalize_name(original) + local str = normalized:match(original or "") + if input.trace > 1 and original ~= str then + input.logger('= normalizer',original.." -> "..str) + end + return str end - return str end -- split the next one up, better for jit @@ -5132,13 +5650,13 @@ function input.automount(instance) end function input.load(instance) - input.start_timing(instance) + input.starttiming(instance) input.identify_cnf(instance) input.load_cnf(instance) input.expand_variables(instance) input.load_hash(instance) input.automount(instance) - input.stop_timing(instance) + input.stoptiming(instance) end function input.for_files(instance, command, files, filetype, mustexist) @@ -5432,7 +5950,7 @@ being written at the same time is small. We also need to extend luatools with a recache feature.</p> --ldx]]-- -caches = caches or { } +caches = caches or { } dir = dir or { } texmf = texmf or { } @@ -5444,9 +5962,20 @@ caches.trace = false caches.tree = false caches.temp = caches.temp or os.getenv("TEXMFCACHE") or os.getenv("HOME") or os.getenv("HOMEPATH") or os.getenv("VARTEXMF") or os.getenv("TEXMFVAR") or os.getenv("TMP") or os.getenv("TEMP") or os.getenv("TMPDIR") or nil caches.paths = caches.paths or { caches.temp } +caches.force = false +input.usecache = not toboolean(os.getenv("TEXMFSHARECACHE") or "false",true) -- true + +if caches.temp and caches.temp ~= "" and lfs.attributes(caches.temp,"mode") ~= "directory" then + if caches.force or io.ask(string.format("Should I create the cache path %s?",caches.temp), "no", { "yes", "no" }) == "yes" then + lfs.mkdirs(caches.temp) + end +end if not caches.temp or caches.temp == "" then - print("\nFATAL ERROR: NO VALID TEMPORARY PATH\n") + print("\nfatal error: there is no valid cache path defined\n") + os.exit() +elseif lfs.attributes(caches.temp,"mode") ~= "directory" then + print(string.format("\nfatal error: cache path %s is not a directory\n",caches.temp)) os.exit() end @@ -5633,8 +6162,6 @@ end -- since we want to use the cache instead of the tree, we will now -- reimplement the saver. -input.usecache = true - function input.aux.save_data(instance, dataname, check) for cachename, files in pairs(instance[dataname]) do local name @@ -5868,12 +6395,143 @@ if lua.bytecode then -- from 0 upwards end +if not modules then modules = { } end modules ['luat-log'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +--[[ldx-- +<p>This is a prelude to a more extensive logging module. For the sake +of parsing log files, in addition to the standard logging we will +provide an <l n='xml'/> structured file. Actually, any logging that +is hooked into callbacks will be \XML\ by default.</p> +--ldx]]-- + +input = input or { } +logs = logs or { } + +--[[ldx-- +<p>This looks pretty ugly but we need to speed things up a bit.</p> +--ldx]]-- + +logs.levels = { + ['error'] = 1, + ['warning'] = 2, + ['info'] = 3, + ['debug'] = 4 +} + +logs.functions = { + 'error', 'warning', 'info', 'debug', 'report', + 'start', 'stop', 'push', 'pop' +} + +logs.callbacks = { + 'start_page_number', + 'stop_page_number', + 'report_output_pages', + 'report_output_log' +} + +logs.xml = logs.xml or { } +logs.tex = logs.tex or { } + +logs.level = 0 + +do + local write_nl, write, format = texio.write_nl or print, texio.write or io.write, string.format + + if texlua then + write_nl = print + write = io.write + end + + function logs.xml.debug(category,str) + if logs.level > 3 then write_nl(format("<d category='%s'>%s</d>",category,str)) end + end + function logs.xml.info(category,str) + if logs.level > 2 then write_nl(format("<i category='%s'>%s</i>",category,str)) end + end + function logs.xml.warning(category,str) + if logs.level > 1 then write_nl(format("<w category='%s'>%s</w>",category,str)) end + end + function logs.xml.error(category,str) + if logs.level > 0 then write_nl(format("<e category='%s'>%s</e>",category,str)) end + end + function logs.xml.report(category,str) + write_nl(format("<r category='%s'>%s</r>",category,str)) + end + + function logs.xml.start() if logs.level > 0 then tw("<%s>" ) end end + function logs.xml.stop () if logs.level > 0 then tw("</%s>") end end + function logs.xml.push () if logs.level > 0 then tw("<!-- ") end end + function logs.xml.pop () if logs.level > 0 then tw(" -->" ) end end + + function logs.tex.debug(category,str) + if logs.level > 3 then write_nl(format("debug >> %s: %s" ,category,str)) end + end + function logs.tex.info(category,str) + if logs.level > 2 then write_nl(format("info >> %s: %s" ,category,str)) end + end + function logs.tex.warning(category,str) + if logs.level > 1 then write_nl(format("warning >> %s: %s",category,str)) end + end + function logs.tex.error(category,str) + if logs.level > 0 then write_nl(format("error >> %s: %s" ,category,str)) end + end + function logs.tex.report(category,str) + write_nl(format("report >> %s: %s" ,category,str)) + end + + function logs.set_level(level) + logs.level = logs.levels[level] or level + end + + function logs.set_method(method) + for _, v in pairs(logs.functions) do + logs[v] = logs[method][v] or function() end + end + if callback and input[method] then + for _, cb in pairs(logs.callbacks) do + callback.register(cb, input[method][cb]) + end + end + end + + function logs.xml.start_page_number() + write_nl(format("<p real='%s' page='%s' sub='%s'", tex.count[0], tex.count[1], tex.count[2])) + end + + function logs.xml.stop_page_number() + write("/>") + write_nl("") + end + + function logs.xml.report_output_pages(p,b) + write_nl(format("<v k='pages' v='%s'/>", p)) + write_nl(format("<v k='bytes' v='%s'/>", b)) + write_nl("") + end + + function logs.xml.report_output_log() + end + +end + +logs.set_level('error') +logs.set_method('tex') + + -- end library merge own = { } own.libs = { -- todo: check which ones are really needed 'l-string.lua', + 'l-lpeg.lua', 'l-table.lua', 'l-io.lua', 'l-md5.lua', @@ -5892,6 +6550,7 @@ own.libs = { -- todo: check which ones are really needed -- 'luat-tex.lua', -- 'luat-kps.lua', 'luat-tmp.lua', + 'luat-log.lua', } -- We need this hack till luatex is fixed. @@ -5953,22 +6612,32 @@ instance.lsrmode = environment.argument("lsr") or false -- use os.env or environment when available -function os.setenv(key,value) - -- todo -end +--~ function input.check_environment(tree) +--~ input.report('') +--~ os.setenv('TMP', os.getenv('TMP') or os.getenv('TEMP') or os.getenv('TMPDIR') or os.getenv('HOME')) +--~ if os.platform == 'linux' then +--~ os.setenv('TEXOS', os.getenv('TEXOS') or 'texmf-linux') +--~ elseif os.platform == 'windows' then +--~ os.setenv('TEXOS', os.getenv('TEXOS') or 'texmf-windows') +--~ elseif os.platform == 'macosx' then +--~ os.setenv('TEXOS', os.getenv('TEXOS') or 'texmf-macosx') +--~ end +--~ os.setenv('TEXOS', string.gsub(string.gsub(os.getenv('TEXOS'),"^[\\\/]*", ''),"[\\\/]*$", '')) +--~ os.setenv('TEXPATH', string.gsub(tree,"\/+$",'')) +--~ os.setenv('TEXMFOS', os.getenv('TEXPATH') .. "/" .. os.getenv('TEXOS')) +--~ input.report('') +--~ input.report("preset : TEXPATH => " .. os.getenv('TEXPATH')) +--~ input.report("preset : TEXOS => " .. os.getenv('TEXOS')) +--~ input.report("preset : TEXMFOS => " .. os.getenv('TEXMFOS')) +--~ input.report("preset : TMP => " .. os.getenv('TMP')) +--~ input.report('') +--~ end function input.check_environment(tree) input.report('') os.setenv('TMP', os.getenv('TMP') or os.getenv('TEMP') or os.getenv('TMPDIR') or os.getenv('HOME')) - if os.platform == 'linux' then - os.setenv('TEXOS', os.getenv('TEXOS') or 'texmf-linux') - elseif os.platform == 'windows' then - os.setenv('TEXOS', os.getenv('TEXOS') or 'texmf-windows') - elseif os.platform == 'macosx' then - os.setenv('TEXOS', os.getenv('TEXOS') or 'texmf-macosx') - end - os.setenv('TEXOS', string.gsub(string.gsub(os.getenv('TEXOS'),"^[\\\/]*", ''),"[\\\/]*$", '')) - os.setenv('TEXPATH', string.gsub(tree,"\/+$",'')) + os.setenv('TEXOS', os.getenv('TEXOS') or ("texmf-" .. os.currentplatform())) + os.setenv('TEXPATH', (tree or "tex"):gsub("\/+$",'')) os.setenv('TEXMFOS', os.getenv('TEXPATH') .. "/" .. os.getenv('TEXOS')) input.report('') input.report("preset : TEXPATH => " .. os.getenv('TEXPATH')) @@ -5985,23 +6654,25 @@ function input.load_environment(name) -- todo: key=value as well as lua if line:find("^[%%%#]") then -- skip comment else - local key, how, value = line:match("^(.-)%s*([%<%=%>%?]+)%s*(.*)%s*$") - value = value:gsub("^%%(.+)%%$", function(v) return os.getenv(v) or "" end) - if how == "=" or how == "<<" then - os.setenv(key,value) - elseif how == "?" or how == "??" then - os.setenv(key,os.getenv(key) or value) - elseif how == "<" or how == "+=" then - if os.getenv(key) then - os.setenv(key,os.getenv(key) .. io.fileseparator .. value) - else - os.setenv(key,value) - end - elseif how == ">" or how == "=+" then - if os.getenv(key) then - os.setenv(key,value .. io.pathseparator .. os.getenv(key)) - else - os.setenv(key,value) + local key, how, value = line:match("^(.-)%s*([<=>%?]+)%s*(.*)%s*$") + if how then + value = value:gsub("%%(.-)%%", function(v) return os.getenv(v) or "" end) + if how == "=" or how == "<<" then + os.setenv(key,value) + elseif how == "?" or how == "??" then + os.setenv(key,os.getenv(key) or value) + elseif how == "<" or how == "+=" then + if os.getenv(key) then + os.setenv(key,os.getenv(key) .. io.fileseparator .. value) + else + os.setenv(key,value) + end + elseif how == ">" or how == "=+" then + if os.getenv(key) then + os.setenv(key,value .. io.pathseparator .. os.getenv(key)) + else + os.setenv(key,value) + end end end end @@ -6013,7 +6684,7 @@ end function input.load_tree(tree) if tree and tree ~= "" then local setuptex = 'setuptex.tmf' - if lfs.attributes(tree, mode) == "directory" then -- check if not nil + if lfs.attributes(tree, "mode") == "directory" then -- check if not nil setuptex = tree .. "/" .. setuptex else setuptex = tree @@ -6094,6 +6765,26 @@ function file.savechecksum(name, checksum) return nil end +function os.currentplatform() + local currentplatform = "linux" + if os.platform == "windows" then + currentplatform = "mswin" + else + local architecture = os.resultof("uname -m") + local unixvariant = os.resultof("uname -s") + if architecture and architecture:find("x86_64") then + currentplatform = "linux-64" + elseif unixvariant and unixvariant:find("Darwin") then + if architecture and architecture:find("i386") then + currentplatform = "osx-intel" + else + currentplatform = "osx-ppc" + end + end + end + return currentplatform +end + -- it starts here input.runners = { } @@ -6401,6 +7092,27 @@ function input.runners.edit_script(instance,filename) end end +function input.runners.save_script_session(filename, list) + local t = { } + for _, key in ipairs(list) do + t[key] = environment.arguments[key] + end + io.savedata(filename,table.serialize(t,true)) +end + +function input.runners.load_script_session(filename) + if lfs.isfile(filename) then + local t = io.loaddata(filename) + if t then + t = loadstring(t) + if t then t = t() end + for key, value in pairs(t) do + environment.arguments[key] = value + end + end + end +end + input.runners.launchers = { windows = { }, unix = { } @@ -6448,6 +7160,15 @@ function input.runners.launch_file(instance,filename) end function input.runners.execute_ctx_script(instance,filename) + local function found(name) + local path = file.dirname(name) + if path and path ~= "" then + return false + else + local fullname = own and own.path and file.join(own.path,name) + return io.exists(fullname) and fullname + end + end local before, after = environment.split_arguments(filename) local suffix = "" if not filename:find("%.lua$") then suffix = ".lua" end @@ -6458,17 +7179,17 @@ function input.runners.execute_ctx_script(instance,filename) -- mtx-<filename> if not fullname or fullname == "" then fullname = "mtx-" .. filename .. suffix - fullname = input.find_file(instance,fullname) + fullname = found(fullname) and input.find_file(instance,fullname) end -- mtx-<filename>s if not fullname or fullname == "" then fullname = "mtx-" .. filename .. "s" .. suffix - fullname = input.find_file(instance,fullname) + fullname = found(fullname) and input.find_file(instance,fullname) end -- mtx-<filename minus trailing s> if not fullname or fullname == "" then fullname = "mtx-" .. filename:gsub("s$","") .. suffix - fullname = input.find_file(instance,fullname) + fullname = found(fullname) and input.find_file(instance,fullname) end -- that should do it if fullname and fullname ~= "" then @@ -6480,8 +7201,23 @@ function input.runners.execute_ctx_script(instance,filename) elseif state == "run" then arg = { } for _,v in pairs(after) do arg[#arg+1] = v end environment.initialize_arguments(arg) +local loadname = environment.arguments['load'] +if loadname then + if type(loadname) ~= "string" then loadname = file.basename(fullname) end + loadname = file.replacesuffix(loadname,"cfg") + input.runners.load_script_session(loadname) +end filename = environment.files[1] + if input.verbose then + input.report("using script: " .. fullname) + end dofile(fullname) +local savename = environment.arguments['save'] +if savename and input.runners.save_list and not table.is_empty(input.runners.save_list or { }) then + if type(savename) ~= "string" then savename = file.basename(fullname) end + savename = file.replacesuffix(savename,"cfg") + input.runners.save_script_session(savename, input.runners.save_list) +end return true end else diff --git a/scripts/context/lua/scite-ctx.lua b/scripts/context/lua/scite-ctx.lua deleted file mode 100644 index 82f8599b1..000000000 --- a/scripts/context/lua/scite-ctx.lua +++ /dev/null @@ -1,924 +0,0 @@ --- version : 1.0.0 - 07/2005 --- author : Hans Hagen - PRAGMA ADE - www.pragma-ade.com --- copyright : public domain or whatever suits --- remark : part of the context distribution - --- todo: name space for local functions - --- loading: scite-ctx.properties - --- # environment variable --- # --- # CTXSPELLPATH=t:/spell --- # --- # auto language detection --- # --- # % version =1.0 language=uk --- # <?xml version='1.0' language='uk' ?> - --- ext.lua.startup.script=$(SciteDefaultHome)/scite-ctx.lua --- --- # extension.$(file.patterns.context)=scite-ctx.lua --- # extension.$(file.patterns.example)=scite-ctx.lua --- --- # ext.lua.reset=1 --- # ext.lua.auto.reload=1 --- # ext.lua.startup.script=t:/lua/scite-ctx.lua --- --- ctx.menulist.default=\ --- wrap=wrap_text|\ --- unwrap=unwrap_text|\ --- sort=sort_text|\ --- document=document_text|\ --- quote=quote_text|\ --- compound=compound_text|\ --- check=check_text --- --- ctx.spellcheck.language=auto --- ctx.spellcheck.wordsize=4 --- ctx.spellcheck.wordpath=ENV(CTXSPELLPATH) --- --- ctx.spellcheck.wordfile.all=spell-uk.txt,spell-nl.txt --- --- ctx.spellcheck.wordfile.uk=spell-uk.txt --- ctx.spellcheck.wordfile.nl=spell-nl.txt --- ctx.spellcheck.wordsize.uk=4 --- ctx.spellcheck.wordsize.nl=4 --- --- command.name.21.*=CTX Action List --- command.subsystem.21.*=3 --- command.21.*=show_menu $(ctx.menulist.default) --- command.groupundo.21.*=yes --- command.shortcut.21.*=Shift+F11 --- --- command.name.22.*=CTX Check Text --- command.subsystem.22.*=3 --- command.22.*=check_text --- command.groupundo.22.*=yes --- command.shortcut.22.*=Ctrl+L --- --- command.name.23.*=CTX Wrap Text --- command.subsystem.23.*=3 --- command.23.*=wrap_text --- command.groupundo.23.*=yes --- command.shortcut.23.*=Ctrl+M --- --- # command.21.*=check_text --- # command.21.*=dofile e:\context\lua\scite-ctx.lua - --- generic functions - -local crlf = "\n" - -function traceln(str) - trace(str .. crlf) - io.flush() -end - -table.len = table.getn -table.join = table.concat - -function table.found(tab, str) - local l, r, p - if string.len(str) == 0 then - return false - else - l, r = 1, table.len(tab) - while l <= r do - p = math.floor((l+r)/2) - if str < tab[p] then - r = p - 1 - elseif str > tab[p] then - l = p + 1 - else - return true - end - end - return false - end -end - -function string.grab(str, delimiter) - local list = {} - for snippet in string.gfind(str,delimiter) do - table.insert(list, snippet) - end - return list -end - -function string.join(list, delimiter) - local size, str = table.len(list), '' - if size > 0 then - str = list[1] - for i = 2, size, 1 do - str = str .. delimiter .. list[i] - end - end - return str -end - -function string.spacy(str) - if string.find(str,"^%s*$") then - return true - else - return false - end -end - -function string.alphacmp(a,b,i) -- slow but ok - if i and i > 0 then - return string.lower(string.gsub(string.sub(a,i),'0',' ')) < string.lower(string.gsub(string.sub(b,i),'0',' ')) - else - return string.lower(a) < string.lower(b) - end -end - -function table.alphasort(list,i) - table.sort(list, function(a,b) return string.alphacmp(a,b,i) end) -end - -function io.exists(filename) - local ok, result, message = pcall(io.open,filename) - if result then - io.close(result) - return true - else - return false - end -end - -function os.envvar(str) - if os.getenv(str) ~= '' then - return os.getenv(str) - elseif os.getenv(string.upper(str)) ~= '' then - return os.getenv(string.upper(str)) - elseif os.getenv(string.lower(str)) ~= '' then - return os.getenv(string.lower(str)) - else - return '' - end -end - -function string.expand(str) - return string.gsub(str, "ENV%((%w+)%)", os.envvar) -end - -function string.strip(str) - return string.gsub(string.gsub(str,"^%s+",''),"%s+$",'') -end - -function string.replace(original,pattern,replacement) - local str = string.gsub(original,pattern,replacement) --- print(str) -- indirect, since else str + nofsubs - return str -- indirect, since else str + nofsubs -end - --- support functions, maybe editor namespace - --- function column_of_position(position) --- local line = editor:LineFromPosition(position) --- local oldposition = editor.CurrentPos --- local column = 0 --- editor:GotoPos(position) --- while editor.CurrentPos ~= 0 and line == editor:LineFromPosition(editor.CurrentPos) do --- editor:CharLeft() --- column = column + 1 --- end --- editor:GotoPos(oldposition) --- if line > 0 then --- return column -1 --- else --- return column --- end --- end - --- function line_of_position(position) --- return editor:LineFromPosition(position) --- end - -function extend_to_start() - local selectionstart = editor.SelectionStart - local selectionend = editor.SelectionEnd - local line = editor:LineFromPosition(selectionstart) - if line > 0 then - while line == editor:LineFromPosition(selectionstart-1) do - selectionstart = selectionstart - 1 - editor:SetSel(selectionstart,selectionend) - end - else - selectionstart = 0 - end - editor:SetSel(selectionstart,selectionend) - return selectionstart -end - -function extend_to_end() -- editor:LineEndExtend() does not work - local selectionstart = editor.SelectionStart - local selectionend = editor.SelectionEnd - local line = editor:LineFromPosition(selectionend) - while line == editor:LineFromPosition(selectionend+1) do - selectionend = selectionend + 1 - editor:SetSel(selectionstart,selectionend) - end - editor:SetSel(selectionstart,selectionend) - return selectionend -end - -function getfiletype() - local firstline = editor:GetLine(0) - if editor.Lexer == SCLEX_TEX then - return 'tex' - elseif editor.Lexer == SCLEX_XML then - return 'xml' - elseif string.find(firstline,"^%%") then - return 'tex' - elseif string.find(firstline,"^<%?xml") then - return 'xml' - else - return 'unknown' - end -end - --- inspired by LuaExt's scite_Files - -function get_dir_list(mask) - local f - if props['PLAT_GTK'] and props['PLAT_GTK'] ~= "" then - f = io.popen('ls -1 ' .. mask) - else - mask = string.gsub(mask, '/','\\') - local tmpfile = 'scite-ctx.tmp' - local cmd = 'dir /b "' .. mask .. '" > ' .. tmpfile - os.execute(cmd) - f = io.open(tmpfile) - end - local files = {} - if not f then -- path check added - return files - end - for line in f:lines() do - table.insert(files, line) - end - f:close() - return files -end - --- banner - -print("loading scite-ctx.lua definition file") -print("") -print("- see scite-ctx.properties for configuring info") -print("") -print("- ctx.spellcheck.wordpath set to " .. props['ctx.spellcheck.wordpath']) -if string.find(string.lower(props['ctx.spellcheck.wordpath']), "ctxspellpath") then - if os.getenv('ctxspellpath') then - print("- ctxspellpath set to " .. os.getenv('CTXSPELLPATH')) - else - print("- 'ctxspellpath is not set") - end - print("- ctx.spellcheck.wordpath expands to " .. string.expand(props['ctx.spellcheck.wordpath'])) -end -print("") -print("- ctx.wraptext.length is set to " .. props['ctx.wraptext.length']) -if props['ctx.helpinfo'] ~= '' then - print("- key bindings:") - print("") - print(string.replace(string.strip(props['ctx.helpinfo']),"%s*\|%s*","\n")) -- indirect, since else str + nofsubs -end -print("") -print("- recognized first lines:") -print("") -print("xml <?xml version='1.0' language='nl'") -print("tex % language=nl") - - --- text functions - --- written while listening to Talk Talk - -local magicstring = string.rep("<ctx-crlf/>", 2) - -function wrap_text() - - -- We always go to the end of a line, so in fact some of - -- the variables set next are not needed. - - local length = props["ctx.wraptext.length"] - - if length == '' then length = 80 else length = tonumber(length) end - - local startposition = editor.SelectionStart - local endposition = editor.SelectionEnd - - if startposition == endposition then return end - - editor:LineEndExtend() - - startposition = editor.SelectionStart - endposition = editor.SelectionEnd - - -- local startline = line_of_position(startposition) - -- local endline = line_of_position(endposition) - -- local startcolumn = column_of_position(startposition) - -- local endcolumn = column_of_position(endposition) - -- - -- editor:SetSel(startposition,endposition) - - local startline = props['SelectionStartLine'] - local endline = props['SelectionEndLine'] - local startcolumn = props['SelectionStartColumn'] - 1 - local endcolumn = props['SelectionEndColumn'] - 1 - - local indentation = string.rep(' ', startcolumn) - local selection = string.gsub(editor:GetSelText(),"[\n\r][\n\r]", "\n") - local selection = string.gsub(selection,"\n\n+", ' ' .. magicstring .. ' ') - local replacement = '' - local templine = '' - - selection = string.gsub(selection,"^%s", '') - - for snippet in string.gfind(selection, "%S+") do - if snippet == magicstring then - replacement = replacement .. templine .. "\n\n" - templine = '' - elseif string.len(templine) + string.len(snippet) > length then - replacement = replacement .. templine .. "\n" - templine = indentation .. snippet - elseif string.len(templine) == 0 then - templine = indentation .. snippet - else - templine = string.len(templine) .. ' ' .. snippet - end - end - - replacement = replacement .. templine - replacement = string.gsub(replacement, "^%s+", '') - - if endcolumn == 0 then - replacement = replacement .. "\n" - end - - editor:ReplaceSel(replacement) - -end - -function unwrap_text() - - local startposition = editor.SelectionStart - local endposition = editor.SelectionEnd - - if startposition == endposition then return end - - editor:HomeExtend() - editor:LineEndExtend() - - startposition = editor.SelectionStart - endposition = editor.SelectionEnd - - local magicstring = string.rep("<multiplelines/>", 2) - local selection = string.gsub(editor:GetSelText(),"[\n\r][\n\r]+", ' ' .. magicstring .. ' ') - local replacement = '' - - for snippet in string.gfind(selection, "%S+") do - if snippet == magicstring then - replacement = replacement .. "\n" - else - replacement = replacement .. snippet .. "\n" - end - end - - if endcolumn == 0 then replacement = replacement .. "\n" end - - editor:ReplaceSel(replacement) - -end - -function sort_text() - - local startposition = editor.SelectionStart - local endposition = editor.SelectionEnd - - if startposition == endposition then return end - - -- local startcolumn = column_of_position(startposition) - -- local endcolumn = column_of_position(endposition) - -- - -- editor:SetSel(startposition,endposition) - - local startline = props['SelectionStartLine'] - local endline = props['SelectionEndLine'] - local startcolumn = props['SelectionStartColumn'] - 1 - local endcolumn = props['SelectionEndColumn'] - 1 - - startposition = extend_to_start() - endposition = extend_to_end() - - local selection = string.gsub(editor:GetSelText(), "%s*$", '') - - list = string.grab(selection,"[^\n\r]+") - table.alphasort(list, startcolumn) - local replacement = table.concat(list, "\n") - - editor:GotoPos(startposition) - editor:SetSel(startposition,endposition) - - if endcolumn == 0 then replacement = replacement .. "\n" end - - editor:ReplaceSel(replacement) - -end - -function document_text() - - local startposition = editor.SelectionStart - local endposition = editor.SelectionEnd - - if startposition == endposition then return end - - startposition = extend_to_start() - endposition = extend_to_end() - - editor:SetSel(startposition,endposition) - - local filetype = getfiletype() - - local replacement = '' - for i = editor:LineFromPosition(startposition), editor:LineFromPosition(endposition) do - local str = editor:GetLine(i) - if filetype == 'xml' then - if string.find(str,"^<%!%-%- .* %-%->%s*$") then - replacement = replacement .. string.gsub(str,"^<%!%-%- (.*) %-%->(%s*)$", "%1\n") - elseif not string.spacy(str) then - replacement = replacement .. '<!-- ' .. string.gsub(str,"(%s*)$", '') .. " -->\n" - else - replacement = replacement .. str - end - else - if string.find(str,"^%%D%s+$") then - replacement = replacement .. "\n" - elseif string.find(str,"^%%D ") then - replacement = replacement .. string.gsub(str,"^%%D ", '') - else - replacement = replacement .. '%D ' .. str - end - end - end - - editor:ReplaceSel(string.gsub(replacement, "[\n\r]$", '')) - -end - -function quote_text() - - local filetype, leftquotation, rightquotation = getfiletype(), '', '' - - if filetype == 'xml' then - leftquotation, rightquotation = "<quotation>", "</quotation>" - leftquote, rightquote = "<quotation>", "</quote>" - else - leftquotation, rightquotation = "\\quotation {", "}" - leftquote, rightquote = "\\quote {", "}" - end - - local replacement = editor:GetSelText() - replacement = string.gsub(replacement, "\`\`(.-)\'\'", leftquotation .. "%1" .. rightquotation) - replacement = string.gsub(replacement, "\"(.-)\"", leftquotation .. "%1" .. rightquotation) - replacement = string.gsub(replacement, "\`(.-)\'", leftquote .. "%1" .. rightquote ) - replacement = string.gsub(replacement, "\'(.-)\'", leftquote .. "%1" .. rightquote ) - editor:ReplaceSel(replacement) - -end - -function compound_text() - - local filetype = getfiletype() - - if filetype == 'xml' then - editor:ReplaceSel(string.gsub(editor:GetSelText(),"(>[^<%-][^<%-]+)([-\/])(%w%w+)","%1<compound token='%2'/>%3")) - else - editor:ReplaceSel(string.gsub(editor:GetSelText(),"([^\|])([-\/]+)([^\|])","%1|%2|%3")) - end - -end - --- written while listening to Alanis Morissette's acoustic --- Jagged Little Pill and Tori Amos' Beekeeper after --- reinstalling on my good old ATH-7 - -local language = props["ctx.spellcheck.language"] -local wordsize = props["ctx.spellcheck.wordsize"] -local wordpath = props["ctx.spellcheck.wordpath"] - -if language == '' then language = 'uk' end -if wordsize == '' then wordsize = 4 else wordsize = tonumber(wordsize) end - -local wordfile = "" -local wordlist = {} -local worddone = 0 - --- we use wordlist as a hash so that we can add entries without the --- need to sort and also use a fast (built in) search - --- function kpsewhich_file(filename,filetype,progname) --- local progflag, typeflag = '', '' --- local tempname = os.tmpname() --- if progname then --- progflag = " --progname=" .. progname .. " " --- end --- if filetype then --- typeflag = " --format=" .. filetype .. " " --- end --- local command = "kpsewhich" .. progflag .. typeflag .. " " .. filename .. " > " .. tempname --- os.execute(command) --- for line in io.lines(tempname) do --- return string.gsub(line, "\s*$", '') --- end --- end - -function check_text() - - local dlanguage = props["ctx.spellcheck.language"] - local dwordsize = props["ctx.spellcheck.wordsize"] - local dwordpath = props["ctx.spellcheck.wordpath"] - - if dlanguage ~= '' then dlanguage = tostring(language) end - if dwordsize ~= '' then dwordsize = tonumber(wordsize) end - - local firstline, skipfirst = editor:GetLine(0), false - local filetype, wordskip, wordgood = getfiletype(), '', '' - - if filetype == 'tex' then - wordskip = "\\" - elseif filetype == 'xml' then - wordskip = "<" - wordgood = ">" - end - - if props["ctx.spellcheck.language"] == 'auto' then - if filetype == 'tex' then - -- % version =1.0 language=uk - firstline = string.gsub(firstline, "^%%%s*", '') - firstline = string.gsub(firstline, "%s*$", '') - for key, val in string.gfind(firstline,"(%w+)=(%w+)") do - if key == "language" then - language = val - traceln("auto document language " .. "'" .. language .. "' (tex)") - end - end - skipfirst = true - elseif filetype == 'xml' then - -- <?xml version='1.0' language='uk' ?> - firstline = string.gsub(firstline, "^%<%?xml%s*", '') - firstline = string.gsub(firstline, "%s*%?%>%s*$", '') - for key, val in string.gfind(firstline,"(%w+)=[\"\'](.-)[\"\']") do - if key == "language" then - language = val - traceln("auto document language " .. "'" .. language .. "' (xml)") - end - end - skipfirst = true - end - end - - local fname = props["ctx.spellcheck.wordfile." .. language] - local fsize = props["ctx.spellcheck.wordsize." .. language] - - if fsize ~= '' then wordsize = tonumber(fsize) end - - if fname ~= '' and fname ~= wordfile then - wordfile, worddone, wordlist = fname, 0, {} - for filename in string.gfind(wordfile,"[^%,]+") do - if wordpath ~= '' then - filename = string.expand(wordpath) .. '/' .. filename - end - if io.exists(filename) then - traceln("loading " .. filename) - for line in io.lines(filename) do - if not string.find(line,"^[\%\#\-]") then - str = string.gsub(line,"%s*$", '') - rawset(wordlist,str,true) -- table.insert(wordlist,str) - worddone = worddone + 1 - end - end - else - traceln("unknown file '" .. filename .."'") - end - end - traceln(worddone .. " words loaded") - end - - reset_text() - - if worddone == 0 then - traceln("no (valid) language or wordfile specified") - else - traceln("start checking") - if wordskip ~= '' then - traceln("ignoring " .. wordskip .. "..." .. wordgood) - end - local i, j, lastpos, startpos, endpos, snippet, len, first = 0, 0, -1, 0, 0, '', 0, 0 - local ok, skip, ch = false, false, '' - if skipfirst then first = string.len(firstline) end - for k = first, editor.TextLength do - ch = editor:textrange(k,k+1) - if wordgood ~= '' and ch == wordgood then - skip = false - elseif ch == wordskip then - skip = true - end - if string.find(ch,"%w") and not string.find(ch,"%d") then - if not skip then - if ok then - endpos = k - else - startpos = k - endpos = k - ok = true - end - end - elseif ok and not skip then - len = endpos - startpos + 1 - if len >= wordsize then - snippet = editor:textrange(startpos,endpos+1) - i = i + 1 - if wordlist[snippet] or wordlist[string.lower(snippet)] then -- table.found(wordlist,snippet) - j = j + 1 - else - editor:StartStyling(startpos,INDICS_MASK) - editor:SetStyling(len,INDIC2_MASK) -- INDIC0_MASK+2 - end - end - ok = false - elseif wordgood == '' then - skip = (ch == wordskip) - end - end - traceln(i .. " words checked, " .. (i-j) .. " errors") - end - -end - -function reset_text() - editor:StartStyling(0,INDICS_MASK) - editor:SetStyling(editor.TextLength,INDIC_PLAIN) -end - --- menu - -local menuactions = {} -local menufunctions = {} - -function UserListShow(menutrigger, menulist) - local menuentries = {} - local list = string.grab(menulist,"[^%|]+") - menuactions = {} - for i=1, table.len(list) do - if list[i] ~= '' then - for key, val in string.gfind(list[i],"%s*(.+)=(.+)%s*") do - table.insert(menuentries,key) - rawset(menuactions,key,val) - end - end - end - local menustring = table.join(menuentries,'|') - if menustring == "" then - traceln("There are no templates defined for this file type.") - else - editor.AutoCSeparator = string.byte('|') - editor:UserListShow(menutrigger,menustring) - editor.AutoCSeparator = string.byte(' ') - end -end - -function OnUserListSelection(trigger,choice) - if menufunctions[trigger] and menuactions[choice] then - return menufunctions[trigger](menuactions[choice]) - else - return false - end -end - --- main menu - -local menutrigger = 12 - -function show_menu(menulist) - UserListShow(menutrigger, menulist) -end - -function process_menu(action) - if not string.find(action,"%(%)$") then - assert(loadstring(action .. "()"))() - else - assert(loadstring(action))() - end -end - -menufunctions[12] = process_menu - --- templates - -local templatetrigger = 13 - --- local ctx_template_paths = { "./ctx-templates", "../ctx-templates", "../../ctx-templates" } --- local ctx_auto_templates = false --- local ctx_template_list = "" --- local ctx_dir_list = { } --- local ctx_dir_name = "./ctx-templates" - --- local ctx_path_list = {} --- local ctx_path_done = {} - --- function ctx_list_loaded() --- return ctx_dir_list and table.getn(ctx_dir_list) > 0 --- end - --- function insert_template(templatelist) --- if props["ctx.template.scan"] == "yes" then --- local current = props["FileDir"] .. "+" .. props["FileExt"] -- no name --- local rescan = props["ctx.template.rescan"] == "yes" --- local suffix = props["ctx.template.suffix."..props["FileExt"]] -- alas, no suffix expansion here --- if rescan then --- print("re-scanning enabled") --- end --- if current ~= ctx_file_path then --- rescan = true --- ctx_file_path = current --- ctx_file_done = false --- ctx_template_list = "" --- end --- if not ctx_file_done or rescan then --- local pattern = "*.*" --- for i, pathname in ipairs(ctx_template_paths) do --- print("scanning " .. pathname .. " for " .. pattern) --- ctx_dir_name = pathname --- ctx_dir_list = get_dir_list(pathname .. "/" .. pattern) --- if ctx_list_loaded() then --- break --- end --- end --- ctx_file_done = true --- end --- if ctx_list_loaded() then --- ctx_template_list = "" --- local pattern = "%." .. suffix .. "$" --- for j, filename in ipairs(ctx_dir_list) do --- if string.find(filename,pattern) then --- local menuname = string.gsub(filename,"%..-$","") --- if ctx_template_list ~= "" then --- ctx_template_list = ctx_template_list .. "|" --- end --- ctx_template_list = ctx_template_list .. menuname .. "=" .. ctx_dir_name .. "/" .. filename --- end --- end --- else --- print("no template files found") --- end --- if ctx_template_list == "" then --- ctx_auto_templates = false --- print("no file related templates found") --- else --- ctx_auto_templates = true --- templatelist = ctx_template_list --- end --- end --- if templatelist ~= "" then --- UserListShow(templatetrigger, templatelist) --- end --- end - -local ctx_template_paths = { "./ctx-templates", "../ctx-templates", "../../ctx-templates" } -local ctx_auto_templates = false -local ctx_template_list = "" - -local ctx_path_list = {} -local ctx_path_done = {} -local ctx_path_name = {} - -function ctx_list_loaded(path) - return ctx_path_list[path] and table.getn(ctx_path_list[path]) > 0 -end - -function insert_template(templatelist) - if props["ctx.template.scan"] == "yes" then - local path = props["FileDir"] - local rescan = props["ctx.template.rescan"] == "yes" - local suffix = props["ctx.template.suffix." .. props["FileExt"]] -- alas, no suffix expansion here - local current = path .. "+" .. props["FileExt"] - if rescan then - print("re-scanning enabled") - end - ctx_template_list = "" - if not ctx_path_done[path] or rescan then - local pattern = "*.*" - for i, pathname in ipairs(ctx_template_paths) do - print("scanning " .. string.gsub(path,"\\","/") .. "/" .. pathname) - ctx_path_name[path] = pathname - ctx_path_list[path] = get_dir_list(pathname .. "/" .. pattern) - if ctx_list_loaded(path) then - print("finished locating template files") - break - end - end - if ctx_list_loaded(path) then - print(table.getn(ctx_path_list[path]) .. " template files found") - else - print("no template files found") - end - end - if ctx_list_loaded(path) then - ctx_template_list = "" - local pattern = "%." .. suffix .. "$" - local n = 0 - for j, filename in ipairs(ctx_path_list[path]) do - if string.find(filename,pattern) then - n = n + 1 - local menuname = string.gsub(filename,"%..-$","") - if ctx_template_list ~= "" then - ctx_template_list = ctx_template_list .. "|" - end - ctx_template_list = ctx_template_list .. menuname .. "=" .. ctx_path_name[path] .. "/" .. filename - end - end - if not ctx_path_done[path] then - print(n .. " suitable template files found") - end - end - ctx_path_done[path] = true - if ctx_template_list == "" then - ctx_auto_templates = false - else - ctx_auto_templates = true - templatelist = ctx_template_list - end - else - ctx_auto_templates = false - end - if templatelist ~= "" then - UserListShow(templatetrigger, templatelist) - end -end - - --- ctx.template.[whatever].[filetype] --- ctx.template.[whatever].data.[filetype] --- ctx.template.[whatever].file.[filetype] --- ctx.template.[whatever].list.[filetype] - -function process_template_one(action) - local text = nil - if ctx_auto_templates then - local f = io.open(action,"r") - if f then - text = string.gsub(f:read("*all"),"\n$","") - f:close() - else - print("unable to auto load template file " .. text) - text = nil - end - end - if not text or text == "" then - text = props["ctx.template." .. action .. ".file"] - if not text or text == "" then - text = props["ctx.template." .. action .. ".data"] - if not text or text == "" then - text = props["ctx.template." .. action] - end - else - local f = io.open(text,"r") - if f then - text = string.gsub(f:read("*all"),"\n$","") - f:close() - else - print("unable to load template file " .. text) - text = nil - end - end - end - if text then - text = string.replace(text,"\\n","\n") - local pos = string.find(text,"%?") - text = string.replace(text,"%?","") - editor:insert(editor.CurrentPos,text) - if pos then - editor.CurrentPos = editor.CurrentPos + pos - 1 - editor.SelectionStart = editor.CurrentPos - editor.SelectionEnd = editor.CurrentPos - editor:GotoPos(editor.CurrentPos) - end - end -end - -menufunctions[13] = process_template_one -menufunctions[14] = process_template_two - --- command.name.26.*=Open Logfile --- command.subsystem.26.*=3 --- command.26.*=open_log --- command.save.before.26.*=2 --- command.groupundo.26.*=yes --- command.shortcut.26.*=Ctrl+E - -function open_log() - scite.Open(props['FileName'] .. ".log") -end diff --git a/scripts/context/ruby/base/kpse.rb b/scripts/context/ruby/base/kpse.rb index a4babae55..0e185b5b8 100644 --- a/scripts/context/ruby/base/kpse.rb +++ b/scripts/context/ruby/base/kpse.rb @@ -64,8 +64,12 @@ module Kpse # @@distribution = 'miktex' if ENV['PATH'] =~ /miktex[\\\/]bin/o - if ENV['PATH'] =~ /(.*?)miktex[\\\/]bin/i then - @@distribution = 'miktex' unless $1 =~ /(texmf\-mswin[\/\\]bin|bin[\/\\]win32)/i + # if ENV['PATH'] =~ /(.*?)miktex[\\\/]bin/i then + # @@distribution = 'miktex' unless $1 =~ /(texmf\-mswin[\/\\]bin|bin[\/\\]win32)/i + # end + + if @@mswindows && (ENV['PATH'] =~ /(.*?)miktex[\\\/]bin/i) then + @@distribution = 'miktex' unless $1 =~ /(texmf\-mswin[\/\\]bin|bin[\/\\]win32)/i end @@re_true = /yes|on|true|1/i diff --git a/scripts/context/ruby/base/tex.rb b/scripts/context/ruby/base/tex.rb index 73b382af9..54d5bc730 100644 --- a/scripts/context/ruby/base/tex.rb +++ b/scripts/context/ruby/base/tex.rb @@ -90,25 +90,7 @@ class TEX @@luafiles = "luafiles.tmp" @@luatarget = "lua/context" - # we now drop pdfetex definitely - - # ENV['PATH'].split(File::PATH_SEPARATOR).each do |p| - # if System.unix? then - # pp, pe = "#{p}/pdftex" , "#{p}/pdfetex" - # else - # pp, pe = "#{p}/pdftex.exe", "#{p}/pdfetex.exe" - # end - # if FileTest.file?(pe) then # we assume no update - # @@pdftex = 'pdfetex' - # break - # elsif FileTest.file?(pp) then # we assume an update - # @@pdftex = 'pdftex' - # break - # end - # end - - # ['etex','pdfetex','standard'] .each do |e| @@texengines[e] = @@pdftex end - # ['tex','pdftex'] .each do |e| @@texengines[e] = 'pdftex' end + @@platformslash = if System.unix? then "\\\\" else "\\" end ['tex','etex','pdftex','pdfetex','standard'] .each do |e| @@texengines[e] = 'pdftex' end ['aleph','omega'] .each do |e| @@texengines[e] = 'aleph' end @@ -120,6 +102,7 @@ class TEX ['pdfetex','pdftex','pdf','pdftex','standard'] .each do |b| @@backends[b] = 'pdftex' end ['dvipdfmx','dvipdfm','dpx','dpm'] .each do |b| @@backends[b] = 'dvipdfmx' end ['xetex','xtx'] .each do |b| @@backends[b] = 'xetex' end + ['aleph'] .each do |b| @@backends[b] = 'dvipdfmx' end ['dvips','ps','dvi'] .each do |b| @@backends[b] = 'dvips' end ['dvipsone'] .each do |b| @@backends[b] = 'dvipsone' end ['acrobat','adobe','distiller'] .each do |b| @@backends[b] = 'acrobat' end @@ -164,11 +147,11 @@ class TEX ['plain','default','standard'] .each do |f| @@mpsmethods[f] = 'plain' end ['metafun'] .each do |f| @@mpsmethods[f] = 'metafun' end - @@texmakestr['plain'] = "\\dump" - @@mpsmakestr['plain'] = "\\dump" + @@texmakestr['plain'] = @@platformslash + "dump" + @@mpsmakestr['plain'] = @@platformslash + "dump" ['cont-en','cont-nl','cont-de','cont-it', - 'cont-fr','cont-cz','cont-ro','cont-uk'] .each do |f| @@texprocstr[f] = "\\emergencyend" end + 'cont-fr','cont-cz','cont-ro','cont-uk'] .each do |f| @@texprocstr[f] = @@platformslash + "emergencyend" end @@runoptions['aleph'] = ['--8bit'] @@runoptions['luatex'] = ['--file-line-error'] @@ -1885,7 +1868,7 @@ end if globalfile || FileTest.file?(rawname) then - if not dummyfile and not globalfile then + if not dummyfile and not globalfile and not forcexml then scantexpreamble(rawname) scantexcontent(rawname) if getvariable('texformats').standard? end diff --git a/scripts/context/ruby/base/texutil.rb b/scripts/context/ruby/base/texutil.rb index 9c43f00e9..4882404d5 100644 --- a/scripts/context/ruby/base/texutil.rb +++ b/scripts/context/ruby/base/texutil.rb @@ -706,8 +706,8 @@ class TeXUtil elsif alpha == @@specialsymbol then character = @@specialbanner elsif alpha.length > 1 then - # character = "\\getvalue\{#{alpha}\}%" - character = "\\#{alpha}%" + # character = "\\getvalue\{#{alpha}\}" + character = "\\#{alpha}" else character = "\\unknown" end diff --git a/scripts/context/ruby/graphics/gs.rb b/scripts/context/ruby/graphics/gs.rb index a73400ba2..cb3d016f4 100644 --- a/scripts/context/ruby/graphics/gs.rb +++ b/scripts/context/ruby/graphics/gs.rb @@ -296,9 +296,9 @@ class GhostScript def gscolorswitch case getvariable('colormodel') - when 'cmyk' then '-dProcessColorModel=/DeviceCMYK ' - when 'rgb' then '-dProcessColorModel=/DeviceRGB ' - when 'gray' then '-dProcessColorModel=/DeviceGRAY ' + when 'cmyk' then '-dProcessColorModel=/DeviceCMYK -dColorConversionStrategy=/CMYK ' + when 'rgb' then '-dProcessColorModel=/DeviceRGB -dColorConversionStrategy=/RGB ' + when 'gray' then '-dProcessColorModel=/DeviceGRAY -dColorConversionStrategy=/GRAY ' else '' end diff --git a/scripts/context/ruby/texexec.rb b/scripts/context/ruby/texexec.rb index 3ba3388f0..d2c722438 100644 --- a/scripts/context/ruby/texexec.rb +++ b/scripts/context/ruby/texexec.rb @@ -111,7 +111,7 @@ class Commands if job = TEX.new(logger) then prepare(job) job.cleanuptemprunfiles - files = @commandline.arguments.sort + files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end if files.length > 0 then if f = File.open(job.tempfilename('tex'),'w') then backspace = @commandline.checkedoption('backspace', '1.5cm') @@ -156,7 +156,7 @@ class Commands prepare(job) job.cleanuptemprunfiles fast = @commandline.option('fast') - files = @commandline.arguments.sort + files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end if fast or (files.length > 0) then if f = File.open(job.tempfilename('tex'),'w') then files.delete("texexec.pdf") @@ -202,7 +202,7 @@ class Commands if job = TEX.new(logger) then prepare(job) job.cleanuptemprunfiles - files = @commandline.arguments.sort + files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end msuffixes = ['tex','mkii','mkiv','mp','pl','pm','rb'] if files.length > 0 then files.each do |fname| @@ -302,7 +302,7 @@ class Commands if job = TEX.new(logger) then prepare(job) job.cleanuptemprunfiles - files = @commandline.arguments.sort + files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end if files.length > 0 then if f = File.open(job.tempfilename('tex'),'w') then emptypages = @commandline.checkedoption('addempty', '') @@ -355,7 +355,7 @@ class Commands if job = TEX.new(logger) then prepare(job) job.cleanuptemprunfiles - files = @commandline.arguments.sort + files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end if files.length > 0 then if f = File.open(job.tempfilename('tex'),'w') then selection = @commandline.checkedoption('selection', '') @@ -425,7 +425,7 @@ class Commands if job = TEX.new(logger) then prepare(job) job.cleanuptemprunfiles - files = @commandline.arguments.sort + files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end if files.length > 0 then if f = File.open(job.tempfilename('tex'),'w') then scale = @commandline.checkedoption('scale') @@ -492,7 +492,7 @@ class Commands if job = TEX.new(logger) then prepare(job) job.cleanuptemprunfiles - files = @commandline.arguments.sort + files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end if files.length > 0 then if f = File.open(job.tempfilename('tex'),'w') then paperoffset = @commandline.checkedoption('paperoffset', '0cm') @@ -762,6 +762,7 @@ commandline.registerflag('aleph') commandline.registerflag('all') commandline.registerflag('fast') +commandline.registerflag('sort') # generic diff --git a/scripts/context/ruby/www/exa.rb b/scripts/context/ruby/www/exa.rb index 997eab67d..20a40fc7b 100644 --- a/scripts/context/ruby/www/exa.rb +++ b/scripts/context/ruby/www/exa.rb @@ -368,6 +368,7 @@ class WWW end def handle_exastatus + get_cfg() # weird, needed for apache, but not for wwwserver if request_variable('id').empty? then if id = valid_session() then send_result() diff --git a/scripts/context/ruby/www/lib.rb b/scripts/context/ruby/www/lib.rb index f5f362b12..b9a44c9f6 100644 --- a/scripts/context/ruby/www/lib.rb +++ b/scripts/context/ruby/www/lib.rb @@ -163,7 +163,7 @@ class WWW @interface.set('template:login' , 'exalogin.htm') @interface.set('process:timeout' , @@session_max_age) @interface.set('process:threshold' , @@send_threshold) - @interface.set('process:background', 'yes') # this demands a watchdog being active + @interface.set('process:background', 'yes') # this demands a watchdog being active @interface.set('process:indirect' , 'no') # indirect download, no direct feed @interface.set('process:autologin' , 'yes') # provide default interface when applicable @interface.set('process:exaurl' , '') # this one will be used as replacement in templates @@ -1226,6 +1226,12 @@ class WWW return ! (@session.nothing?('gui') && @session.nothing?('path') && @session.nothing?('process')) end + def get_cfg() + if data = load_interface_file() then + fetch_session_interface_variables(data) + end + end + end class WWW |