├── README.md ├── README_zh_CN.md ├── automation ├── autoload │ └── OneClickRubyV2.lua └── include │ ├── json.lua │ ├── json │ ├── decode.lua │ ├── decode │ │ ├── composite.lua │ │ ├── number.lua │ │ ├── others.lua │ │ ├── state.lua │ │ ├── strings.lua │ │ └── util.lua │ ├── encode.lua │ ├── encode │ │ ├── array.lua │ │ ├── calls.lua │ │ ├── number.lua │ │ ├── object.lua │ │ ├── others.lua │ │ ├── output.lua │ │ ├── output_utility.lua │ │ └── strings.lua │ └── util.lua │ ├── luajit-request │ ├── init.lua │ └── luajit-curl.lua │ └── utf8.lua └── libcurl.dll /README.md: -------------------------------------------------------------------------------- 1 | # RubyTools 2 | Generate and add ruby in Aegisub. 3 | With karaoke support. 4 | [Chinese Ver(中文说明)](README_zh_CN.md) 5 | ## Usage 6 | - Clone this repository. 7 | - Unzip all the files to the root folder of Aegisub. 8 | - Start your Aegisub and check the automation menu. 9 | - Run "One Click Ruby" for selected lines. 10 | 11 | ## Requirements(Included) 12 | - luajit-request 13 | https://github.com/LPGhatguy/luajit-request 14 | **NOTE:** You need a 64bit **_libcurl.dll_** for corresponding Aegisub. 15 | - json (for v2) 16 | https://github.com/rxi/json.lua 17 | - utf8.lua 18 | https://github.com/Stepets/utf8.lua 19 | -------------------------------------------------------------------------------- /README_zh_CN.md: -------------------------------------------------------------------------------- 1 | # RubyTools 2 | 在 Aegisub 中为日文字幕添加假名标注。 3 | 已支持卡拉OK字幕。 4 | [英文说明(English Ver)](README.md) 5 | ## 用法 6 | - Clone 本仓库 7 | - 解压所有文件到 Aegisub 根目录。 8 | - 启动你的 Aegisub 并检查自动化菜单 9 | - 在选择行上运行 "One Click Ruby" 即可。 10 | 11 | ## 依赖(部分已包含于本仓库) 12 | - luajit-request 13 | https://github.com/LPGhatguy/luajit-request 14 | **注意:** 你需要自行寻找64位版本的 **_libcurl.dll_** 来在对应版本的 Aegisub 中使用本工具。 15 | - json (v2版) 16 | https://github.com/rxi/json.lua 17 | - utf8.lua 18 | https://github.com/Stepets/utf8.lua 19 | -------------------------------------------------------------------------------- /automation/autoload/OneClickRubyV2.lua: -------------------------------------------------------------------------------- 1 | script_name = "One Click Ruby" 2 | script_description = "Get the formatted lyrics by Yahoo's API and ruby them" 3 | script_author = "domo" 4 | ruby_part_from = "Kage Maboroshi&KiNen" 5 | script_version = "2.2" 6 | 7 | require "karaskel" 8 | local request = require("luajit-request") 9 | local ffi = require"ffi" 10 | local utf8 = require"utf8" 11 | local json = require"json" 12 | -- local Y = require"Yutils" 13 | -- local tts = Y.table.tostring 14 | meta = nil; 15 | styles = nil; 16 | --Typesetting Parameters-- 17 | rubypadding = 0 --extra spacing of ruby chars 18 | rubyscale = 0.5 --scale of ruby chars 19 | 20 | --Separators-- 21 | char_s = "##" -- s(tart) of ruby part 22 | char_m = "|<" -- m(iddle) which divides the whole part into kanji and furigana 23 | char_e = "##" -- e(nd) of ruby part 24 | 25 | 26 | local function deleteEmpty(tbl) 27 | for i=#tbl,1,-1 do 28 | if tbl[i] == "" then 29 | table.remove(tbl, i) 30 | end 31 | end 32 | return tbl 33 | end 34 | 35 | function Split(szFullString, szSeparator) 36 | local nFindStartIndex = 1 37 | local nSplitIndex = 1 38 | local nSplitArray = {} 39 | while true do 40 | local nFindLastIndex = string.find(szFullString, szSeparator, nFindStartIndex) 41 | if not nFindLastIndex then 42 | nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex, string.len(szFullString)) 43 | break 44 | end 45 | nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex, nFindLastIndex - 1) 46 | nFindStartIndex = nFindLastIndex + string.len(szSeparator) 47 | nSplitIndex = nSplitIndex + 1 48 | end 49 | return nSplitArray 50 | end 51 | 52 | function addK0BeforeText(s) 53 | local result = "" 54 | local i = 1 55 | while i <= utf8.len(s) do 56 | local charC = utf8.sub(s, i, i) 57 | if charC == "{" then 58 | local j = i 59 | while utf8.sub(s, j, j) ~= "}" and j <= utf8.len(s) do 60 | j = j + 1 61 | end 62 | result = result .. utf8.sub(s, i, j) 63 | i = j + 1 64 | else 65 | if i == 1 or utf8.sub(s, i-1, i-1) ~= "}" then 66 | result = result .. "{\\k0}" 67 | end 68 | result = result .. charC 69 | i = i + 1 70 | end 71 | end 72 | return result 73 | end 74 | 75 | local function send2YahooV2(sentence,appid,grade) 76 | local url = "https://jlp.yahooapis.jp/FuriganaService/V2/furigana" 77 | params = {["q"] = sentence, 78 | ["grade"] = grade} 79 | data = {["id"] = "1234-1", 80 | ["jsonrpc"] = "2.0", 81 | ["method"] = "jlp.furiganaservice.furigana", 82 | ["params"] = params} 83 | local result, err, message = request.send(url,{ 84 | method = "POST", 85 | headers = {['content-type'] = "application/x-www-form-urlencoded", 86 | ["User-Agent"] = "Yahoo AppID: " .. appid}, 87 | data = json.encode(data)} 88 | ) 89 | if (not result) then aegisub.debug.out(err, message) end 90 | -- aegisub.debug.out(result.body) 91 | return result.body 92 | end 93 | 94 | local function KaraText(newText,lineKara) 95 | rubyTbl = deleteEmpty(Split(newText,char_s)) 96 | newRubyTbl = {} 97 | for i=1,#rubyTbl do 98 | if string.find(rubyTbl[i],char_m) then 99 | newRubyTbl[#newRubyTbl+1] = rubyTbl[i] 100 | else 101 | for j=1,utf8.len(rubyTbl[i]) do 102 | newRubyTbl[#newRubyTbl+1] = utf8.sub(rubyTbl[i],j,j) 103 | end 104 | end 105 | end 106 | -- aegisub.debug.out(tts(newRubyTbl).."\n") 107 | sylNum = #lineKara 108 | for i=#newRubyTbl,2,-1 do 109 | realWord = string.match(newRubyTbl[i],"([^|<]+)[<|]?") 110 | if utf8.len(realWord)= math.huge then 94 | error("unexpected number value '" .. tostring(val) .. "'") 95 | end 96 | return string.format("%.14g", val) 97 | end 98 | 99 | 100 | local type_func_map = { 101 | [ "nil" ] = encode_nil, 102 | [ "table" ] = encode_table, 103 | [ "string" ] = encode_string, 104 | [ "number" ] = encode_number, 105 | [ "boolean" ] = tostring, 106 | } 107 | 108 | 109 | encode = function(val, stack) 110 | local t = type(val) 111 | local f = type_func_map[t] 112 | if f then 113 | return f(val, stack) 114 | end 115 | error("unexpected type '" .. t .. "'") 116 | end 117 | 118 | 119 | function json.encode(val) 120 | return ( encode(val) ) 121 | end 122 | 123 | 124 | ------------------------------------------------------------------------------- 125 | -- Decode 126 | ------------------------------------------------------------------------------- 127 | 128 | local parse 129 | 130 | local function create_set(...) 131 | local res = {} 132 | for i = 1, select("#", ...) do 133 | res[ select(i, ...) ] = true 134 | end 135 | return res 136 | end 137 | 138 | local space_chars = create_set(" ", "\t", "\r", "\n") 139 | local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") 140 | local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") 141 | local literals = create_set("true", "false", "null") 142 | 143 | local literal_map = { 144 | [ "true" ] = true, 145 | [ "false" ] = false, 146 | [ "null" ] = nil, 147 | } 148 | 149 | 150 | local function next_char(str, idx, set, negate) 151 | for i = idx, #str do 152 | if set[str:sub(i, i)] ~= negate then 153 | return i 154 | end 155 | end 156 | return #str + 1 157 | end 158 | 159 | 160 | local function decode_error(str, idx, msg) 161 | local line_count = 1 162 | local col_count = 1 163 | for i = 1, idx - 1 do 164 | col_count = col_count + 1 165 | if str:sub(i, i) == "\n" then 166 | line_count = line_count + 1 167 | col_count = 1 168 | end 169 | end 170 | error( string.format("%s at line %d col %d", msg, line_count, col_count) ) 171 | end 172 | 173 | 174 | local function codepoint_to_utf8(n) 175 | -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa 176 | local f = math.floor 177 | if n <= 0x7f then 178 | return string.char(n) 179 | elseif n <= 0x7ff then 180 | return string.char(f(n / 64) + 192, n % 64 + 128) 181 | elseif n <= 0xffff then 182 | return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) 183 | elseif n <= 0x10ffff then 184 | return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, 185 | f(n % 4096 / 64) + 128, n % 64 + 128) 186 | end 187 | error( string.format("invalid unicode codepoint '%x'", n) ) 188 | end 189 | 190 | 191 | local function parse_unicode_escape(s) 192 | local n1 = tonumber( s:sub(3, 6), 16 ) 193 | local n2 = tonumber( s:sub(9, 12), 16 ) 194 | -- Surrogate pair? 195 | if n2 then 196 | return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) 197 | else 198 | return codepoint_to_utf8(n1) 199 | end 200 | end 201 | 202 | 203 | local function parse_string(str, i) 204 | local has_unicode_escape = false 205 | local has_surrogate_escape = false 206 | local has_escape = false 207 | local last 208 | for j = i + 1, #str do 209 | local x = str:byte(j) 210 | 211 | if x < 32 then 212 | decode_error(str, j, "control character in string") 213 | end 214 | 215 | if last == 92 then -- "\\" (escape char) 216 | if x == 117 then -- "u" (unicode escape sequence) 217 | local hex = str:sub(j + 1, j + 5) 218 | if not hex:find("%x%x%x%x") then 219 | decode_error(str, j, "invalid unicode escape in string") 220 | end 221 | if hex:find("^[dD][89aAbB]") then 222 | has_surrogate_escape = true 223 | else 224 | has_unicode_escape = true 225 | end 226 | else 227 | local c = string.char(x) 228 | if not escape_chars[c] then 229 | decode_error(str, j, "invalid escape char '" .. c .. "' in string") 230 | end 231 | has_escape = true 232 | end 233 | last = nil 234 | 235 | elseif x == 34 then -- '"' (end of string) 236 | local s = str:sub(i + 1, j - 1) 237 | if has_surrogate_escape then 238 | s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape) 239 | end 240 | if has_unicode_escape then 241 | s = s:gsub("\\u....", parse_unicode_escape) 242 | end 243 | if has_escape then 244 | s = s:gsub("\\.", escape_char_map_inv) 245 | end 246 | return s, j + 1 247 | 248 | else 249 | last = x 250 | end 251 | end 252 | decode_error(str, i, "expected closing quote for string") 253 | end 254 | 255 | 256 | local function parse_number(str, i) 257 | local x = next_char(str, i, delim_chars) 258 | local s = str:sub(i, x - 1) 259 | local n = tonumber(s) 260 | if not n then 261 | decode_error(str, i, "invalid number '" .. s .. "'") 262 | end 263 | return n, x 264 | end 265 | 266 | 267 | local function parse_literal(str, i) 268 | local x = next_char(str, i, delim_chars) 269 | local word = str:sub(i, x - 1) 270 | if not literals[word] then 271 | decode_error(str, i, "invalid literal '" .. word .. "'") 272 | end 273 | return literal_map[word], x 274 | end 275 | 276 | 277 | local function parse_array(str, i) 278 | local res = {} 279 | local n = 1 280 | i = i + 1 281 | while 1 do 282 | local x 283 | i = next_char(str, i, space_chars, true) 284 | -- Empty / end of array? 285 | if str:sub(i, i) == "]" then 286 | i = i + 1 287 | break 288 | end 289 | -- Read token 290 | x, i = parse(str, i) 291 | res[n] = x 292 | n = n + 1 293 | -- Next token 294 | i = next_char(str, i, space_chars, true) 295 | local chr = str:sub(i, i) 296 | i = i + 1 297 | if chr == "]" then break end 298 | if chr ~= "," then decode_error(str, i, "expected ']' or ','") end 299 | end 300 | return res, i 301 | end 302 | 303 | 304 | local function parse_object(str, i) 305 | local res = {} 306 | i = i + 1 307 | while 1 do 308 | local key, val 309 | i = next_char(str, i, space_chars, true) 310 | -- Empty / end of object? 311 | if str:sub(i, i) == "}" then 312 | i = i + 1 313 | break 314 | end 315 | -- Read key 316 | if str:sub(i, i) ~= '"' then 317 | decode_error(str, i, "expected string for key") 318 | end 319 | key, i = parse(str, i) 320 | -- Read ':' delimiter 321 | i = next_char(str, i, space_chars, true) 322 | if str:sub(i, i) ~= ":" then 323 | decode_error(str, i, "expected ':' after key") 324 | end 325 | i = next_char(str, i + 1, space_chars, true) 326 | -- Read value 327 | val, i = parse(str, i) 328 | -- Set 329 | res[key] = val 330 | -- Next token 331 | i = next_char(str, i, space_chars, true) 332 | local chr = str:sub(i, i) 333 | i = i + 1 334 | if chr == "}" then break end 335 | if chr ~= "," then decode_error(str, i, "expected '}' or ','") end 336 | end 337 | return res, i 338 | end 339 | 340 | 341 | local char_func_map = { 342 | [ '"' ] = parse_string, 343 | [ "0" ] = parse_number, 344 | [ "1" ] = parse_number, 345 | [ "2" ] = parse_number, 346 | [ "3" ] = parse_number, 347 | [ "4" ] = parse_number, 348 | [ "5" ] = parse_number, 349 | [ "6" ] = parse_number, 350 | [ "7" ] = parse_number, 351 | [ "8" ] = parse_number, 352 | [ "9" ] = parse_number, 353 | [ "-" ] = parse_number, 354 | [ "t" ] = parse_literal, 355 | [ "f" ] = parse_literal, 356 | [ "n" ] = parse_literal, 357 | [ "[" ] = parse_array, 358 | [ "{" ] = parse_object, 359 | } 360 | 361 | 362 | parse = function(str, idx) 363 | local chr = str:sub(idx, idx) 364 | local f = char_func_map[chr] 365 | if f then 366 | return f(str, idx) 367 | end 368 | decode_error(str, idx, "unexpected character '" .. chr .. "'") 369 | end 370 | 371 | 372 | function json.decode(str) 373 | if type(str) ~= "string" then 374 | error("expected argument of type string, got " .. type(str)) 375 | end 376 | return ( parse(str, next_char(str, 1, space_chars, true)) ) 377 | end 378 | 379 | 380 | return json 381 | -------------------------------------------------------------------------------- /automation/include/json/decode.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local lpeg = require("lpeg") 6 | 7 | local error = error 8 | local pcall = pcall 9 | 10 | local jsonutil = require("json.util") 11 | local merge = jsonutil.merge 12 | local util = require("json.decode.util") 13 | 14 | local decode_state = require("json.decode.state") 15 | 16 | local setmetatable, getmetatable = setmetatable, getmetatable 17 | local assert = assert 18 | local ipairs, pairs = ipairs, pairs 19 | local string_char = require("string").char 20 | 21 | local type = type 22 | 23 | local require = require 24 | 25 | local _ENV = nil 26 | 27 | local modulesToLoad = { 28 | "composite", 29 | "strings", 30 | "number", 31 | "others" 32 | } 33 | local loadedModules = { 34 | } 35 | 36 | local json_decode = {} 37 | 38 | json_decode.default = { 39 | unicodeWhitespace = true, 40 | initialObject = false, 41 | nothrow = false 42 | } 43 | 44 | local modes_defined = { "default", "strict", "simple" } 45 | 46 | json_decode.simple = {} 47 | 48 | json_decode.strict = { 49 | unicodeWhitespace = true, 50 | initialObject = true, 51 | nothrow = false 52 | } 53 | 54 | for _,name in ipairs(modulesToLoad) do 55 | local mod = require("json.decode." .. name) 56 | if mod.mergeOptions then 57 | for _, mode in pairs(modes_defined) do 58 | mod.mergeOptions(json_decode[mode], mode) 59 | end 60 | end 61 | loadedModules[#loadedModules + 1] = mod 62 | end 63 | 64 | -- Shift over default into defaultOptions to permit build optimization 65 | local defaultOptions = json_decode.default 66 | json_decode.default = nil 67 | 68 | local function generateDecoder(lexer, options) 69 | -- Marker to permit detection of final end 70 | local marker = {} 71 | local parser = lpeg.Ct((options.ignored * lexer)^0 * lpeg.Cc(marker)) * options.ignored * (lpeg.P(-1) + util.unexpected()) 72 | local decoder = function(data) 73 | local state = decode_state.create(options) 74 | local parsed = parser:match(data) 75 | assert(parsed, "Invalid JSON data") 76 | local i = 0 77 | while true do 78 | i = i + 1 79 | local item = parsed[i] 80 | if item == marker then break end 81 | if type(item) == 'function' and item ~= jsonutil.undefined and item ~= jsonutil.null then 82 | item(state) 83 | else 84 | state:set_value(item) 85 | end 86 | end 87 | if options.initialObject then 88 | assert(type(state.previous) == 'table', "Initial value not an object or array") 89 | end 90 | -- Make sure stack is empty 91 | assert(state.i == 0, "Unclosed elements present") 92 | return state.previous 93 | end 94 | if options.nothrow then 95 | return function(data) 96 | local status, rv = pcall(decoder, data) 97 | if status then 98 | return rv 99 | else 100 | return nil, rv 101 | end 102 | end 103 | end 104 | return decoder 105 | end 106 | 107 | local function buildDecoder(mode) 108 | mode = mode and merge({}, defaultOptions, mode) or defaultOptions 109 | for _, mod in ipairs(loadedModules) do 110 | if mod.mergeOptions then 111 | mod.mergeOptions(mode) 112 | end 113 | end 114 | local ignored = mode.unicodeWhitespace and util.unicode_ignored or util.ascii_ignored 115 | -- Store 'ignored' in the global options table 116 | mode.ignored = ignored 117 | 118 | --local grammar = { 119 | -- [1] = mode.initialObject and (ignored * (object_type + array_type)) or value_type 120 | --} 121 | local lexer 122 | for _, mod in ipairs(loadedModules) do 123 | local new_lexer = mod.generateLexer(mode) 124 | lexer = lexer and lexer + new_lexer or new_lexer 125 | end 126 | return generateDecoder(lexer, mode) 127 | end 128 | 129 | -- Since 'default' is nil, we cannot take map it 130 | local defaultDecoder = buildDecoder(json_decode.default) 131 | local prebuilt_decoders = {} 132 | for _, mode in pairs(modes_defined) do 133 | if json_decode[mode] ~= nil then 134 | prebuilt_decoders[json_decode[mode]] = buildDecoder(json_decode[mode]) 135 | end 136 | end 137 | 138 | --[[ 139 | Options: 140 | number => number decode options 141 | string => string decode options 142 | array => array decode options 143 | object => object decode options 144 | initialObject => whether or not to require the initial object to be a table/array 145 | allowUndefined => whether or not to allow undefined values 146 | ]] 147 | local function getDecoder(mode) 148 | mode = mode == true and json_decode.strict or mode or json_decode.default 149 | local decoder = mode == nil and defaultDecoder or prebuilt_decoders[mode] 150 | if decoder then 151 | return decoder 152 | end 153 | return buildDecoder(mode) 154 | end 155 | 156 | local function decode(data, mode) 157 | local decoder = getDecoder(mode) 158 | return decoder(data) 159 | end 160 | 161 | local mt = {} 162 | mt.__call = function(self, ...) 163 | return decode(...) 164 | end 165 | 166 | json_decode.getDecoder = getDecoder 167 | json_decode.decode = decode 168 | json_decode.util = util 169 | setmetatable(json_decode, mt) 170 | 171 | return json_decode 172 | -------------------------------------------------------------------------------- /automation/include/json/decode/composite.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local pairs = pairs 6 | local type = type 7 | 8 | local lpeg = require("lpeg") 9 | 10 | local util = require("json.decode.util") 11 | local jsonutil = require("json.util") 12 | 13 | local rawset = rawset 14 | 15 | local assert = assert 16 | local tostring = tostring 17 | 18 | local error = error 19 | local getmetatable = getmetatable 20 | 21 | local _ENV = nil 22 | 23 | local defaultOptions = { 24 | array = { 25 | trailingComma = true 26 | }, 27 | object = { 28 | trailingComma = true, 29 | number = true, 30 | identifier = true, 31 | setObjectKey = rawset 32 | }, 33 | calls = { 34 | defs = nil, 35 | -- By default, do not allow undefined calls to be de-serialized as call objects 36 | allowUndefined = false 37 | } 38 | } 39 | 40 | local modeOptions = { 41 | default = nil, 42 | strict = { 43 | array = { 44 | trailingComma = false 45 | }, 46 | object = { 47 | trailingComma = false, 48 | number = false, 49 | identifier = false 50 | } 51 | } 52 | } 53 | 54 | local function BEGIN_ARRAY(state) 55 | state:push() 56 | state:new_array() 57 | end 58 | local function END_ARRAY(state) 59 | state:end_array() 60 | state:pop() 61 | end 62 | 63 | local function BEGIN_OBJECT(state) 64 | state:push() 65 | state:new_object() 66 | end 67 | local function END_OBJECT(state) 68 | state:end_object() 69 | state:pop() 70 | end 71 | 72 | local function END_CALL(state) 73 | state:end_call() 74 | state:pop() 75 | end 76 | 77 | local function SET_KEY(state) 78 | state:set_key() 79 | end 80 | 81 | local function NEXT_VALUE(state) 82 | state:put_value() 83 | end 84 | 85 | local function mergeOptions(options, mode) 86 | jsonutil.doOptionMerge(options, true, 'array', defaultOptions, mode and modeOptions[mode]) 87 | jsonutil.doOptionMerge(options, true, 'object', defaultOptions, mode and modeOptions[mode]) 88 | jsonutil.doOptionMerge(options, true, 'calls', defaultOptions, mode and modeOptions[mode]) 89 | end 90 | 91 | 92 | local isPattern 93 | if lpeg.type then 94 | function isPattern(value) 95 | return lpeg.type(value) == 'pattern' 96 | end 97 | else 98 | local metaAdd = getmetatable(lpeg.P("")).__add 99 | function isPattern(value) 100 | return getmetatable(value).__add == metaAdd 101 | end 102 | end 103 | 104 | 105 | local function generateSingleCallLexer(name, func) 106 | if type(name) ~= 'string' and not isPattern(name) then 107 | error("Invalid functionCalls name: " .. tostring(name) .. " not a string or LPEG pattern") 108 | end 109 | -- Allow boolean or function to match up w/ encoding permissions 110 | if type(func) ~= 'boolean' and type(func) ~= 'function' then 111 | error("Invalid functionCalls item: " .. name .. " not a function") 112 | end 113 | local function buildCallCapture(name) 114 | return function(state) 115 | if func == false then 116 | error("Function call on '" .. name .. "' not permitted") 117 | end 118 | state:push() 119 | state:new_call(name, func) 120 | end 121 | end 122 | local nameCallCapture 123 | if type(name) == 'string' then 124 | nameCallCapture = lpeg.P(name .. "(") * lpeg.Cc(name) / buildCallCapture 125 | else 126 | -- Name matcher expected to produce a capture 127 | nameCallCapture = name * "(" / buildCallCapture 128 | end 129 | -- Call func over nameCallCapture and value to permit function receiving name 130 | return nameCallCapture 131 | end 132 | 133 | local function generateNamedCallLexers(options) 134 | if not options.calls or not options.calls.defs then 135 | return 136 | end 137 | local callCapture 138 | for name, func in pairs(options.calls.defs) do 139 | local newCapture = generateSingleCallLexer(name, func) 140 | if not callCapture then 141 | callCapture = newCapture 142 | else 143 | callCapture = callCapture + newCapture 144 | end 145 | end 146 | return callCapture 147 | end 148 | 149 | local function generateCallLexer(options) 150 | local lexer 151 | local namedCapture = generateNamedCallLexers(options) 152 | if options.calls and options.calls.allowUndefined then 153 | lexer = generateSingleCallLexer(lpeg.C(util.identifier), true) 154 | end 155 | if namedCapture then 156 | lexer = lexer and lexer + namedCapture or namedCapture 157 | end 158 | if lexer then 159 | lexer = lexer + lpeg.P(")") * lpeg.Cc(END_CALL) 160 | end 161 | return lexer 162 | end 163 | 164 | local function generateLexer(options) 165 | local ignored = options.ignored 166 | local array_options, object_options = options.array, options.object 167 | local lexer = 168 | lpeg.P("[") * lpeg.Cc(BEGIN_ARRAY) 169 | + lpeg.P("]") * lpeg.Cc(END_ARRAY) 170 | + lpeg.P("{") * lpeg.Cc(BEGIN_OBJECT) 171 | + lpeg.P("}") * lpeg.Cc(END_OBJECT) 172 | + lpeg.P(":") * lpeg.Cc(SET_KEY) 173 | + lpeg.P(",") * lpeg.Cc(NEXT_VALUE) 174 | if object_options.identifier then 175 | -- Add identifier match w/ validation check that it is in key 176 | lexer = lexer + lpeg.C(util.identifier) * ignored * lpeg.P(":") * lpeg.Cc(SET_KEY) 177 | end 178 | local callLexers = generateCallLexer(options) 179 | if callLexers then 180 | lexer = lexer + callLexers 181 | end 182 | return lexer 183 | end 184 | 185 | local composite = { 186 | mergeOptions = mergeOptions, 187 | generateLexer = generateLexer 188 | } 189 | 190 | return composite 191 | -------------------------------------------------------------------------------- /automation/include/json/decode/number.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local lpeg = require("lpeg") 6 | local tonumber = tonumber 7 | local jsonutil = require("json.util") 8 | local merge = jsonutil.merge 9 | local util = require("json.decode.util") 10 | 11 | local _ENV = nil 12 | 13 | local digit = lpeg.R("09") 14 | local digits = digit^1 15 | 16 | -- Illegal octal declaration 17 | local illegal_octal_detect = #(lpeg.P('0') * digits) * util.denied("Octal numbers") 18 | 19 | local int = (lpeg.P('-') + 0) * (lpeg.R("19") * digits + illegal_octal_detect + digit) 20 | 21 | local frac = lpeg.P('.') * digits 22 | 23 | local exp = lpeg.S("Ee") * (lpeg.S("-+") + 0) * digits 24 | 25 | local nan = lpeg.S("Nn") * lpeg.S("Aa") * lpeg.S("Nn") 26 | local inf = lpeg.S("Ii") * lpeg.P("nfinity") 27 | local ninf = lpeg.P('-') * lpeg.S("Ii") * lpeg.P("nfinity") 28 | local hex = (lpeg.P("0x") + lpeg.P("0X")) * lpeg.R("09","AF","af")^1 29 | 30 | local defaultOptions = { 31 | nan = true, 32 | inf = true, 33 | frac = true, 34 | exp = true, 35 | hex = false 36 | } 37 | 38 | local modeOptions = {} 39 | 40 | modeOptions.strict = { 41 | nan = false, 42 | inf = false 43 | } 44 | 45 | local nan_value = 0/0 46 | local inf_value = 1/0 47 | local ninf_value = -1/0 48 | 49 | --[[ 50 | Options: configuration options for number rules 51 | nan: match NaN 52 | inf: match Infinity 53 | frac: match fraction portion (.0) 54 | exp: match exponent portion (e1) 55 | DEFAULT: nan, inf, frac, exp 56 | ]] 57 | local function mergeOptions(options, mode) 58 | jsonutil.doOptionMerge(options, false, 'number', defaultOptions, mode and modeOptions[mode]) 59 | end 60 | 61 | local function generateLexer(options) 62 | options = options.number 63 | local ret = int 64 | if options.frac then 65 | ret = ret * (frac + 0) 66 | else 67 | ret = ret * (#frac * util.denied("Fractions", "number.frac") + 0) 68 | end 69 | if options.exp then 70 | ret = ret * (exp + 0) 71 | else 72 | ret = ret * (#exp * util.denied("Exponents", "number.exp") + 0) 73 | end 74 | if options.hex then 75 | ret = hex + ret 76 | else 77 | ret = #hex * util.denied("Hexadecimal", "number.hex") + ret 78 | end 79 | -- Capture number now 80 | ret = ret / tonumber 81 | if options.nan then 82 | ret = ret + nan / function() return nan_value end 83 | else 84 | ret = ret + #nan * util.denied("NaN", "number.nan") 85 | end 86 | if options.inf then 87 | ret = ret + ninf / function() return ninf_value end + inf / function() return inf_value end 88 | else 89 | ret = ret + (#ninf + #inf) * util.denied("+/-Inf", "number.inf") 90 | end 91 | return ret 92 | end 93 | 94 | local number = { 95 | int = int, 96 | mergeOptions = mergeOptions, 97 | generateLexer = generateLexer 98 | } 99 | 100 | return number 101 | -------------------------------------------------------------------------------- /automation/include/json/decode/others.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local lpeg = require("lpeg") 6 | local jsonutil = require("json.util") 7 | local merge = jsonutil.merge 8 | local util = require("json.decode.util") 9 | 10 | -- Container module for other JavaScript types (bool, null, undefined) 11 | 12 | local _ENV = nil 13 | 14 | -- For null and undefined, use the util.null value to preserve null-ness 15 | local booleanCapture = 16 | lpeg.P("true") * lpeg.Cc(true) 17 | + lpeg.P("false") * lpeg.Cc(false) 18 | 19 | local nullCapture = lpeg.P("null") 20 | local undefinedCapture = lpeg.P("undefined") 21 | 22 | local defaultOptions = { 23 | allowUndefined = true, 24 | null = jsonutil.null, 25 | undefined = jsonutil.undefined 26 | } 27 | 28 | local modeOptions = {} 29 | 30 | modeOptions.simple = { 31 | null = false, -- Mapped to nil 32 | undefined = false -- Mapped to nil 33 | } 34 | modeOptions.strict = { 35 | allowUndefined = false 36 | } 37 | 38 | local function mergeOptions(options, mode) 39 | jsonutil.doOptionMerge(options, false, 'others', defaultOptions, mode and modeOptions[mode]) 40 | end 41 | 42 | local function generateLexer(options) 43 | -- The 'or nil' clause allows false to map to a nil value since 'nil' cannot be merged 44 | options = options.others 45 | local valueCapture = ( 46 | booleanCapture 47 | + nullCapture * lpeg.Cc(options.null or nil) 48 | ) 49 | if options.allowUndefined then 50 | valueCapture = valueCapture + undefinedCapture * lpeg.Cc(options.undefined or nil) 51 | else 52 | valueCapture = valueCapture + #undefinedCapture * util.denied("undefined", "others.allowUndefined") 53 | end 54 | return valueCapture 55 | end 56 | 57 | local others = { 58 | mergeOptions = mergeOptions, 59 | generateLexer = generateLexer 60 | } 61 | 62 | return others 63 | -------------------------------------------------------------------------------- /automation/include/json/decode/state.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | 6 | local setmetatable = setmetatable 7 | local jsonutil = require("json.util") 8 | local assert = assert 9 | local type = type 10 | local next = next 11 | local unpack = unpack 12 | 13 | local _ENV = nil 14 | 15 | local state_ops = {} 16 | local state_mt = { 17 | __index = state_ops 18 | } 19 | 20 | function state_ops.pop(self) 21 | self.previous_set = true 22 | self.previous = self.active 23 | local i = self.i 24 | -- Load in this array into the active item 25 | self.active = self.stack[i] 26 | self.active_state = self.state_stack[i] 27 | self.active_key = self.key_stack[i] 28 | self.stack[i] = nil 29 | self.state_stack[i] = nil 30 | self.key_stack[i] = nil 31 | 32 | self.i = i - 1 33 | end 34 | 35 | function state_ops.push(self) 36 | local i = self.i + 1 37 | self.i = i 38 | 39 | self.stack[i] = self.active 40 | self.state_stack[i] = self.active_state 41 | self.key_stack[i] = self.active_key 42 | end 43 | 44 | function state_ops.put_object_value(self, trailing) 45 | local object_options = self.options.object 46 | if trailing and object_options.trailingComma then 47 | if not self.active_key then 48 | return 49 | end 50 | end 51 | assert(self.active_key, "Missing key value") 52 | object_options.setObjectKey(self.active, self.active_key, self:grab_value()) 53 | self.active_key = nil 54 | end 55 | 56 | function state_ops.put_array_value(self, trailing) 57 | -- Safety check 58 | if trailing and not self.previous_set and self.options.array.trailingComma then 59 | return 60 | end 61 | local new_index = self.active_state + 1 62 | self.active_state = new_index 63 | self.active[new_index] = self:grab_value() 64 | end 65 | 66 | function state_ops.put_value(self, trailing) 67 | if self.active_state == 'object' then 68 | self:put_object_value(trailing) 69 | else 70 | self:put_array_value(trailing) 71 | end 72 | end 73 | 74 | function state_ops.new_array(self) 75 | local new_array = {} 76 | if jsonutil.InitArray then 77 | new_array = jsonutil.InitArray(new_array) or new_array 78 | end 79 | self.active = new_array 80 | self.active_state = 0 81 | self.active_key = nil 82 | self:unset_value() 83 | end 84 | 85 | function state_ops.end_array(self) 86 | if self.previous_set or self.active_state ~= 0 then 87 | -- Not an empty array 88 | self:put_value(true) 89 | end 90 | if self.active_state ~= #self.active then 91 | -- Store the length in 92 | self.active.n = self.active_state 93 | end 94 | end 95 | 96 | function state_ops.new_object(self) 97 | local new_object = {} 98 | self.active = new_object 99 | self.active_state = 'object' 100 | self.active_key = nil 101 | self:unset_value() 102 | end 103 | 104 | function state_ops.end_object(self) 105 | if self.previous_set or next(self.active) then 106 | -- Not an empty object 107 | self:put_value(true) 108 | end 109 | end 110 | 111 | function state_ops.new_call(self, name, func) 112 | -- TODO setup properly 113 | local new_call = {} 114 | new_call.name = name 115 | new_call.func = func 116 | self.active = new_call 117 | self.active_state = 0 118 | self.active_key = nil 119 | self:unset_value() 120 | end 121 | 122 | function state_ops.end_call(self) 123 | if self.previous_set or self.active_state ~= 0 then 124 | -- Not an empty array 125 | self:put_value(true) 126 | end 127 | if self.active_state ~= #self.active then 128 | -- Store the length in 129 | self.active.n = self.active_state 130 | end 131 | local func = self.active.func 132 | if func == true then 133 | func = jsonutil.buildCall 134 | end 135 | self.active = func(self.active.name, unpack(self.active, 1, self.active.n or #self.active)) 136 | end 137 | 138 | 139 | function state_ops.unset_value(self) 140 | self.previous_set = false 141 | self.previous = nil 142 | end 143 | 144 | function state_ops.grab_value(self) 145 | assert(self.previous_set, "Previous value not set") 146 | self.previous_set = false 147 | return self.previous 148 | end 149 | 150 | function state_ops.set_value(self, value) 151 | assert(not self.previous_set, "Value set when one already in slot") 152 | self.previous_set = true 153 | self.previous = value 154 | end 155 | 156 | function state_ops.set_key(self) 157 | assert(self.active_state == 'object', "Cannot set key on array") 158 | local value = self:grab_value() 159 | local value_type = type(value) 160 | if self.options.object.number then 161 | assert(value_type == 'string' or value_type == 'number', "As configured, a key must be a number or string") 162 | else 163 | assert(value_type == 'string', "As configured, a key must be a string") 164 | end 165 | self.active_key = value 166 | end 167 | 168 | 169 | local function create(options) 170 | local ret = { 171 | options = options, 172 | stack = {}, 173 | state_stack = {}, 174 | key_stack = {}, 175 | i = 0, 176 | active = nil, 177 | active_key = nil, 178 | previous = nil, 179 | active_state = nil 180 | 181 | } 182 | return setmetatable(ret, state_mt) 183 | end 184 | 185 | local state = { 186 | create = create 187 | } 188 | 189 | return state 190 | -------------------------------------------------------------------------------- /automation/include/json/decode/strings.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local lpeg = require("lpeg") 6 | local jsonutil = require("json.util") 7 | local util = require("json.decode.util") 8 | local merge = jsonutil.merge 9 | 10 | local tonumber = tonumber 11 | local string_char = require("string").char 12 | local floor = require("math").floor 13 | local table_concat = require("table").concat 14 | 15 | local error = error 16 | 17 | local _ENV = nil 18 | 19 | local function get_error(item) 20 | local fmt_string = item .. " in string [%q] @ %i:%i" 21 | return lpeg.P(function(data, index) 22 | local line, line_index, bad_char, last_line = util.get_invalid_character_info(data, index) 23 | local err = fmt_string:format(bad_char, line, line_index) 24 | error(err) 25 | end) * 1 26 | end 27 | 28 | local bad_unicode = get_error("Illegal unicode escape") 29 | local bad_hex = get_error("Illegal hex escape") 30 | local bad_character = get_error("Illegal character") 31 | local bad_escape = get_error("Illegal escape") 32 | 33 | local knownReplacements = { 34 | ["'"] = "'", 35 | ['"'] = '"', 36 | ['\\'] = '\\', 37 | ['/'] = '/', 38 | b = '\b', 39 | f = '\f', 40 | n = '\n', 41 | r = '\r', 42 | t = '\t', 43 | v = '\v', 44 | z = '\z' 45 | } 46 | 47 | -- according to the table at http://da.wikipedia.org/wiki/UTF-8 48 | local function utf8DecodeUnicode(code1, code2) 49 | code1, code2 = tonumber(code1, 16), tonumber(code2, 16) 50 | if code1 == 0 and code2 < 0x80 then 51 | return string_char(code2) 52 | end 53 | if code1 < 0x08 then 54 | return string_char( 55 | 0xC0 + code1 * 4 + floor(code2 / 64), 56 | 0x80 + code2 % 64) 57 | end 58 | return string_char( 59 | 0xE0 + floor(code1 / 16), 60 | 0x80 + (code1 % 16) * 4 + floor(code2 / 64), 61 | 0x80 + code2 % 64) 62 | end 63 | 64 | local function decodeX(code) 65 | code = tonumber(code, 16) 66 | return string_char(code) 67 | end 68 | 69 | local doSimpleSub = lpeg.C(lpeg.S("'\"\\/bfnrtvz")) / knownReplacements 70 | local doUniSub = lpeg.P('u') * (lpeg.C(util.hexpair) * lpeg.C(util.hexpair) + bad_unicode) 71 | local doXSub = lpeg.P('x') * (lpeg.C(util.hexpair) + bad_hex) 72 | 73 | local defaultOptions = { 74 | badChars = '', 75 | additionalEscapes = false, -- disallow untranslated escapes 76 | escapeCheck = #lpeg.S('bfnrtv/\\"xu\'z'), -- no check on valid characters 77 | decodeUnicode = utf8DecodeUnicode, 78 | strict_quotes = false 79 | } 80 | 81 | local modeOptions = {} 82 | 83 | modeOptions.strict = { 84 | badChars = '\b\f\n\r\t\v', 85 | additionalEscapes = false, -- no additional escapes 86 | escapeCheck = #lpeg.S('bfnrtv/\\"u'), --only these chars are allowed to be escaped 87 | strict_quotes = true 88 | } 89 | 90 | local function mergeOptions(options, mode) 91 | jsonutil.doOptionMerge(options, false, 'strings', defaultOptions, mode and modeOptions[mode]) 92 | end 93 | 94 | local function buildCaptureString(quote, badChars, escapeMatch) 95 | local captureChar = (1 - lpeg.S("\\" .. badChars .. quote)) + (lpeg.P("\\") / "" * escapeMatch) 96 | -- During error, force end 97 | local captureString = captureChar^0 + (-#lpeg.P(quote) * bad_character + -1) 98 | return lpeg.P(quote) * lpeg.Cs(captureString) * lpeg.P(quote) 99 | end 100 | 101 | local function generateLexer(options) 102 | options = options.strings 103 | local quotes = { '"' } 104 | if not options.strict_quotes then 105 | quotes[#quotes + 1] = "'" 106 | end 107 | local escapeMatch = doSimpleSub 108 | escapeMatch = escapeMatch + doXSub / decodeX 109 | escapeMatch = escapeMatch + doUniSub / options.decodeUnicode 110 | if options.additionalEscapes then 111 | escapeMatch = escapeMatch + options.additionalEscapes 112 | end 113 | if options.escapeCheck then 114 | escapeMatch = options.escapeCheck * escapeMatch + bad_escape 115 | end 116 | local captureString 117 | for i = 1, #quotes do 118 | local cap = buildCaptureString(quotes[i], options.badChars, escapeMatch) 119 | if captureString == nil then 120 | captureString = cap 121 | else 122 | captureString = captureString + cap 123 | end 124 | end 125 | return captureString 126 | end 127 | 128 | local strings = { 129 | mergeOptions = mergeOptions, 130 | generateLexer = generateLexer 131 | } 132 | 133 | return strings 134 | -------------------------------------------------------------------------------- /automation/include/json/decode/util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local lpeg = require("lpeg") 6 | local select = select 7 | local pairs, ipairs = pairs, ipairs 8 | local tonumber = tonumber 9 | local string_char = require("string").char 10 | local rawset = rawset 11 | local jsonutil = require("json.util") 12 | 13 | local error = error 14 | local setmetatable = setmetatable 15 | 16 | local table_concat = require("table").concat 17 | 18 | local merge = require("json.util").merge 19 | 20 | local _ENV = nil 21 | 22 | local function get_invalid_character_info(input, index) 23 | local parsed = input:sub(1, index) 24 | local bad_character = input:sub(index, index) 25 | local _, line_number = parsed:gsub('\n',{}) 26 | local last_line = parsed:match("\n([^\n]+.)$") or parsed 27 | return line_number, #last_line, bad_character, last_line 28 | end 29 | 30 | local function build_report(msg) 31 | local fmt = msg:gsub("%%", "%%%%") .. " @ character: %i %i:%i [%s] line:\n%s" 32 | return lpeg.P(function(data, pos) 33 | local line, line_index, bad_char, last_line = get_invalid_character_info(data, pos) 34 | local text = fmt:format(pos, line, line_index, bad_char, last_line) 35 | error(text) 36 | end) * 1 37 | end 38 | local function unexpected() 39 | local msg = "unexpected character" 40 | return build_report(msg) 41 | end 42 | local function expected(...) 43 | local items = {...} 44 | local msg 45 | if #items > 1 then 46 | msg = "expected one of '" .. table_concat(items, "','") .. "'" 47 | else 48 | msg = "expected '" .. items[1] .. "'" 49 | end 50 | return build_report(msg) 51 | end 52 | local function denied(item, option) 53 | local msg 54 | if option then 55 | msg = ("'%s' denied by option set '%s'"):format(item, option) 56 | else 57 | msg = ("'%s' denied"):format(item) 58 | end 59 | return build_report(msg) 60 | end 61 | 62 | -- 09, 0A, 0B, 0C, 0D, 20 63 | local ascii_space = lpeg.S("\t\n\v\f\r ") 64 | local unicode_space 65 | do 66 | local chr = string_char 67 | local u_space = ascii_space 68 | -- \u0085 \u00A0 69 | u_space = u_space + lpeg.P(chr(0xC2)) * lpeg.S(chr(0x85) .. chr(0xA0)) 70 | -- \u1680 \u180E 71 | u_space = u_space + lpeg.P(chr(0xE1)) * (lpeg.P(chr(0x9A, 0x80)) + chr(0xA0, 0x8E)) 72 | -- \u2000 - \u200A, also 200B 73 | local spacing_end = "" 74 | for i = 0x80,0x8b do 75 | spacing_end = spacing_end .. chr(i) 76 | end 77 | -- \u2028 \u2029 \u202F 78 | spacing_end = spacing_end .. chr(0xA8) .. chr(0xA9) .. chr(0xAF) 79 | u_space = u_space + lpeg.P(chr(0xE2, 0x80)) * lpeg.S(spacing_end) 80 | -- \u205F 81 | u_space = u_space + lpeg.P(chr(0xE2, 0x81, 0x9F)) 82 | -- \u3000 83 | u_space = u_space + lpeg.P(chr(0xE3, 0x80, 0x80)) 84 | -- BOM \uFEFF 85 | u_space = u_space + lpeg.P(chr(0xEF, 0xBB, 0xBF)) 86 | unicode_space = u_space 87 | end 88 | 89 | local identifier = lpeg.R("AZ","az","__") * lpeg.R("AZ","az", "__", "09") ^0 90 | 91 | local hex = lpeg.R("09","AF","af") 92 | local hexpair = hex * hex 93 | 94 | local comments = { 95 | cpp = lpeg.P("//") * (1 - lpeg.P("\n"))^0 * lpeg.P("\n"), 96 | c = lpeg.P("/*") * (1 - lpeg.P("*/"))^0 * lpeg.P("*/") 97 | } 98 | 99 | local comment = comments.cpp + comments.c 100 | 101 | local ascii_ignored = (ascii_space + comment)^0 102 | 103 | local unicode_ignored = (unicode_space + comment)^0 104 | 105 | -- Parse the lpeg version skipping patch-values 106 | -- LPEG <= 0.7 have no version value... so 0.7 is value 107 | local DecimalLpegVersion = lpeg.version and tonumber(lpeg.version():match("^(%d+%.%d+)")) or 0.7 108 | 109 | local function setObjectKeyForceNumber(t, key, value) 110 | key = tonumber(key) or key 111 | return rawset(t, key, value) 112 | end 113 | 114 | local util = { 115 | unexpected = unexpected, 116 | expected = expected, 117 | denied = denied, 118 | ascii_space = ascii_space, 119 | unicode_space = unicode_space, 120 | identifier = identifier, 121 | hex = hex, 122 | hexpair = hexpair, 123 | comments = comments, 124 | comment = comment, 125 | ascii_ignored = ascii_ignored, 126 | unicode_ignored = unicode_ignored, 127 | DecimalLpegVersion = DecimalLpegVersion, 128 | get_invalid_character_info = get_invalid_character_info, 129 | setObjectKeyForceNumber = setObjectKeyForceNumber 130 | } 131 | 132 | return util 133 | -------------------------------------------------------------------------------- /automation/include/json/encode.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local type = type 6 | local assert, error = assert, error 7 | local getmetatable, setmetatable = getmetatable, setmetatable 8 | 9 | local ipairs, pairs = ipairs, pairs 10 | local require = require 11 | 12 | local output = require("json.encode.output") 13 | 14 | local util = require("json.util") 15 | local util_merge, isCall = util.merge, util.isCall 16 | 17 | local _ENV = nil 18 | 19 | --[[ 20 | List of encoding modules to load. 21 | Loaded in sequence such that earlier encoders get priority when 22 | duplicate type-handlers exist. 23 | ]] 24 | local modulesToLoad = { 25 | "strings", 26 | "number", 27 | "calls", 28 | "others", 29 | "array", 30 | "object" 31 | } 32 | -- Modules that have been loaded 33 | local loadedModules = {} 34 | 35 | local json_encode = {} 36 | 37 | -- Configuration bases for client apps 38 | local modes_defined = { "default", "strict" } 39 | 40 | json_encode.default = {} 41 | json_encode.strict = { 42 | initialObject = true -- Require an object at the root 43 | } 44 | 45 | -- For each module, load it and its defaults 46 | for _,name in ipairs(modulesToLoad) do 47 | local mod = require("json.encode." .. name) 48 | if mod.mergeOptions then 49 | for _, mode in pairs(modes_defined) do 50 | mod.mergeOptions(json_encode[mode], mode) 51 | end 52 | end 53 | loadedModules[name] = mod 54 | end 55 | 56 | -- Merges values, assumes all tables are arrays, inner values flattened, optionally constructing output 57 | local function flattenOutput(out, values) 58 | out = not out and {} or type(out) == 'table' and out or {out} 59 | if type(values) == 'table' then 60 | for _, v in ipairs(values) do 61 | out[#out + 1] = v 62 | end 63 | else 64 | out[#out + 1] = values 65 | end 66 | return out 67 | end 68 | 69 | -- Prepares the encoding map from the already provided modules and new config 70 | local function prepareEncodeMap(options) 71 | local map = {} 72 | for _, name in ipairs(modulesToLoad) do 73 | local encodermap = loadedModules[name].getEncoder(options[name]) 74 | for valueType, encoderSet in pairs(encodermap) do 75 | map[valueType] = flattenOutput(map[valueType], encoderSet) 76 | end 77 | end 78 | return map 79 | end 80 | 81 | --[[ 82 | Encode a value with a given encoding map and state 83 | ]] 84 | local function encodeWithMap(value, map, state, isObjectKey) 85 | local t = type(value) 86 | local encoderList = assert(map[t], "Failed to encode value, unhandled type: " .. t) 87 | for _, encoder in ipairs(encoderList) do 88 | local ret = encoder(value, state, isObjectKey) 89 | if false ~= ret then 90 | return ret 91 | end 92 | end 93 | error("Failed to encode value, encoders for " .. t .. " deny encoding") 94 | end 95 | 96 | 97 | local function getBaseEncoder(options) 98 | local encoderMap = prepareEncodeMap(options) 99 | if options.preProcess then 100 | local preProcess = options.preProcess 101 | return function(value, state, isObjectKey) 102 | local ret = preProcess(value, isObjectKey or false) 103 | if nil ~= ret then 104 | value = ret 105 | end 106 | return encodeWithMap(value, encoderMap, state) 107 | end 108 | end 109 | return function(value, state, isObjectKey) 110 | return encodeWithMap(value, encoderMap, state) 111 | end 112 | end 113 | --[[ 114 | Retreive an initial encoder instance based on provided options 115 | the initial encoder is responsible for initializing state 116 | State has at least these values configured: encode, check_unique, already_encoded 117 | ]] 118 | function json_encode.getEncoder(options) 119 | options = options and util_merge({}, json_encode.default, options) or json_encode.default 120 | local encode = getBaseEncoder(options) 121 | 122 | local function initialEncode(value) 123 | if options.initialObject then 124 | local errorMessage = "Invalid arguments: expects a JSON Object or Array at the root" 125 | assert(type(value) == 'table' and not isCall(value, options), errorMessage) 126 | end 127 | 128 | local alreadyEncoded = {} 129 | local function check_unique(value) 130 | assert(not alreadyEncoded[value], "Recursive encoding of value") 131 | alreadyEncoded[value] = true 132 | end 133 | 134 | local outputEncoder = options.output and options.output() or output.getDefault() 135 | local state = { 136 | encode = encode, 137 | check_unique = check_unique, 138 | already_encoded = alreadyEncoded, -- To unmark encoding when moving up stack 139 | outputEncoder = outputEncoder 140 | } 141 | local ret = encode(value, state) 142 | if nil ~= ret then 143 | return outputEncoder.simple and outputEncoder.simple(ret) or ret 144 | end 145 | end 146 | return initialEncode 147 | end 148 | 149 | -- CONSTRUCT STATE WITH FOLLOWING (at least) 150 | --[[ 151 | encoder 152 | check_unique -- used by inner encoders to make sure value is unique 153 | already_encoded -- used to unmark a value as unique 154 | ]] 155 | function json_encode.encode(data, options) 156 | return json_encode.getEncoder(options)(data) 157 | end 158 | 159 | local mt = {} 160 | mt.__call = function(self, ...) 161 | return json_encode.encode(...) 162 | end 163 | 164 | setmetatable(json_encode, mt) 165 | 166 | return json_encode 167 | -------------------------------------------------------------------------------- /automation/include/json/encode/array.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local jsonutil = require("json.util") 6 | 7 | local type = type 8 | local pairs = pairs 9 | local assert = assert 10 | 11 | local table = require("table") 12 | local math = require("math") 13 | local table_concat = table.concat 14 | local math_floor, math_modf = math.floor, math.modf 15 | 16 | local jsonutil = require("json.util") 17 | local util_IsArray = jsonutil.IsArray 18 | 19 | local _ENV = nil 20 | 21 | local defaultOptions = { 22 | isArray = util_IsArray 23 | } 24 | 25 | local modeOptions = {} 26 | 27 | local function mergeOptions(options, mode) 28 | jsonutil.doOptionMerge(options, false, 'array', defaultOptions, mode and modeOptions[mode]) 29 | end 30 | 31 | --[[ 32 | Utility function to determine whether a table is an array or not. 33 | Criteria for it being an array: 34 | * ExternalIsArray returns true (or false directly reports not-array) 35 | * If the table has an 'n' value that is an integer >= 1 then it 36 | is an array... may result in false positives (should check some values 37 | before it) 38 | * It is a contiguous list of values with zero string-based keys 39 | ]] 40 | local function isArray(val, options) 41 | local externalIsArray = options and options.isArray 42 | 43 | if externalIsArray then 44 | local ret = externalIsArray(val) 45 | if ret == true or ret == false then 46 | return ret 47 | end 48 | end 49 | -- Use the 'n' element if it's a number 50 | if type(val.n) == 'number' and math_floor(val.n) == val.n and val.n >= 1 then 51 | return true 52 | end 53 | local len = #val 54 | for k,v in pairs(val) do 55 | if type(k) ~= 'number' then 56 | return false 57 | end 58 | local _, decim = math_modf(k) 59 | if not (decim == 0 and 1<=k) then 60 | return false 61 | end 62 | if k > len then -- Use Lua's length as absolute determiner 63 | return false 64 | end 65 | end 66 | 67 | return true 68 | end 69 | 70 | --[[ 71 | Cleanup function to unmark a value as in the encoding process and return 72 | trailing results 73 | ]] 74 | local function unmarkAfterEncode(tab, state, ...) 75 | state.already_encoded[tab] = nil 76 | return ... 77 | end 78 | local function getEncoder(options) 79 | options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions 80 | local function encodeArray(tab, state) 81 | if not isArray(tab, options) then 82 | return false 83 | end 84 | -- Make sure this value hasn't been encoded yet 85 | state.check_unique(tab) 86 | local encode = state.encode 87 | local compositeEncoder = state.outputEncoder.composite 88 | local valueEncoder = [[ 89 | for i = 1, (composite.n or #composite) do 90 | local val = composite[i] 91 | PUTINNER(i ~= 1) 92 | val = encode(val, state) 93 | val = val or '' 94 | if val then 95 | PUTVALUE(val) 96 | end 97 | end 98 | ]] 99 | return unmarkAfterEncode(tab, state, compositeEncoder(valueEncoder, '[', ']', ',', tab, encode, state)) 100 | end 101 | return { table = encodeArray } 102 | end 103 | 104 | local array = { 105 | mergeOptions = mergeOptions, 106 | isArray = isArray, 107 | getEncoder = getEncoder 108 | } 109 | 110 | return array 111 | -------------------------------------------------------------------------------- /automation/include/json/encode/calls.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local table = require("table") 6 | local table_concat = table.concat 7 | 8 | local select = select 9 | local getmetatable, setmetatable = getmetatable, setmetatable 10 | local assert = assert 11 | 12 | local jsonutil = require("json.util") 13 | 14 | local isCall, decodeCall = jsonutil.isCall, jsonutil.decodeCall 15 | 16 | local _ENV = nil 17 | 18 | local defaultOptions = { 19 | } 20 | 21 | -- No real default-option handling needed... 22 | local modeOptions = {} 23 | 24 | local function mergeOptions(options, mode) 25 | jsonutil.doOptionMerge(options, false, 'calls', defaultOptions, mode and modeOptions[mode]) 26 | end 27 | 28 | 29 | --[[ 30 | Encodes 'value' as a function call 31 | Must have parameters in the 'callData' field of the metatable 32 | name == name of the function call 33 | parameters == array of parameters to encode 34 | ]] 35 | local function getEncoder(options) 36 | options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions 37 | local function encodeCall(value, state) 38 | if not isCall(value) then 39 | return false 40 | end 41 | local encode = state.encode 42 | local name, params = decodeCall(value) 43 | local compositeEncoder = state.outputEncoder.composite 44 | local valueEncoder = [[ 45 | for i = 1, (composite.n or #composite) do 46 | local val = composite[i] 47 | PUTINNER(i ~= 1) 48 | val = encode(val, state) 49 | val = val or '' 50 | if val then 51 | PUTVALUE(val) 52 | end 53 | end 54 | ]] 55 | return compositeEncoder(valueEncoder, name .. '(', ')', ',', params, encode, state) 56 | end 57 | return { 58 | table = encodeCall, 59 | ['function'] = encodeCall 60 | } 61 | end 62 | 63 | local calls = { 64 | mergeOptions = mergeOptions, 65 | getEncoder = getEncoder 66 | } 67 | 68 | return calls 69 | -------------------------------------------------------------------------------- /automation/include/json/encode/number.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local tostring = tostring 6 | local assert = assert 7 | local jsonutil = require("json.util") 8 | local huge = require("math").huge 9 | 10 | local _ENV = nil 11 | 12 | local defaultOptions = { 13 | nan = true, 14 | inf = true 15 | } 16 | 17 | local modeOptions = {} 18 | modeOptions.strict = { 19 | nan = false, 20 | inf = false 21 | } 22 | 23 | local function mergeOptions(options, mode) 24 | jsonutil.doOptionMerge(options, false, 'number', defaultOptions, mode and modeOptions[mode]) 25 | end 26 | 27 | 28 | local function encodeNumber(number, options) 29 | if number ~= number then 30 | assert(options.nan, "Invalid number: NaN not enabled") 31 | return "NaN" 32 | end 33 | if number == huge then 34 | assert(options.inf, "Invalid number: Infinity not enabled") 35 | return "Infinity" 36 | end 37 | if number == -huge then 38 | assert(options.inf, "Invalid number: Infinity not enabled") 39 | return "-Infinity" 40 | end 41 | return tostring(number) 42 | end 43 | 44 | local function getEncoder(options) 45 | options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions 46 | return { 47 | number = function(number, state) 48 | return encodeNumber(number, options) 49 | end 50 | } 51 | end 52 | 53 | local number = { 54 | mergeOptions = mergeOptions, 55 | getEncoder = getEncoder 56 | } 57 | 58 | return number 59 | -------------------------------------------------------------------------------- /automation/include/json/encode/object.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local pairs = pairs 6 | local assert = assert 7 | 8 | local type = type 9 | local tostring = tostring 10 | 11 | local table_concat = require("table").concat 12 | local jsonutil = require("json.util") 13 | 14 | local _ENV = nil 15 | 16 | local defaultOptions = { 17 | } 18 | 19 | local modeOptions = {} 20 | 21 | local function mergeOptions(options, mode) 22 | jsonutil.doOptionMerge(options, false, 'object', defaultOptions, mode and modeOptions[mode]) 23 | end 24 | 25 | --[[ 26 | Cleanup function to unmark a value as in the encoding process and return 27 | trailing results 28 | ]] 29 | local function unmarkAfterEncode(tab, state, ...) 30 | state.already_encoded[tab] = nil 31 | return ... 32 | end 33 | --[[ 34 | Encode a table as a JSON Object ( keys = strings, values = anything else ) 35 | ]] 36 | local function encodeTable(tab, options, state) 37 | -- Make sure this value hasn't been encoded yet 38 | state.check_unique(tab) 39 | local encode = state.encode 40 | local compositeEncoder = state.outputEncoder.composite 41 | local valueEncoder = [[ 42 | local first = true 43 | for k, v in pairs(composite) do 44 | local ti = type(k) 45 | assert(ti == 'string' or ti == 'number' or ti == 'boolean', "Invalid object index type: " .. ti) 46 | local name = encode(tostring(k), state, true) 47 | if first then 48 | first = false 49 | else 50 | name = ',' .. name 51 | end 52 | PUTVALUE(name .. ':') 53 | local val = encode(v, state) 54 | val = val or '' 55 | if val then 56 | PUTVALUE(val) 57 | end 58 | end 59 | ]] 60 | return unmarkAfterEncode(tab, state, compositeEncoder(valueEncoder, '{', '}', nil, tab, encode, state)) 61 | end 62 | 63 | local function getEncoder(options) 64 | options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions 65 | return { 66 | table = function(tab, state) 67 | return encodeTable(tab, options, state) 68 | end 69 | } 70 | end 71 | 72 | local object = { 73 | mergeOptions = mergeOptions, 74 | getEncoder = getEncoder 75 | } 76 | 77 | return object 78 | -------------------------------------------------------------------------------- /automation/include/json/encode/others.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local tostring = tostring 6 | 7 | local assert = assert 8 | local jsonutil = require("json.util") 9 | local type = type 10 | 11 | local _ENV = nil 12 | 13 | -- Shortcut that works 14 | local encodeBoolean = tostring 15 | 16 | local defaultOptions = { 17 | allowUndefined = true, 18 | null = jsonutil.null, 19 | undefined = jsonutil.undefined 20 | } 21 | 22 | local modeOptions = {} 23 | 24 | modeOptions.strict = { 25 | allowUndefined = false 26 | } 27 | 28 | local function mergeOptions(options, mode) 29 | jsonutil.doOptionMerge(options, false, 'others', defaultOptions, mode and modeOptions[mode]) 30 | end 31 | local function getEncoder(options) 32 | options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions 33 | local function encodeOthers(value, state) 34 | if value == options.null then 35 | return 'null' 36 | elseif value == options.undefined then 37 | assert(options.allowUndefined, "Invalid value: Unsupported 'Undefined' parameter") 38 | return 'undefined' 39 | else 40 | return false 41 | end 42 | end 43 | local function encodeBoolean(value, state) 44 | return value and 'true' or 'false' 45 | end 46 | local nullType = type(options.null) 47 | local undefinedType = options.undefined and type(options.undefined) 48 | -- Make sure that all of the types handled here are handled 49 | local ret = { 50 | boolean = encodeBoolean, 51 | ['nil'] = function() return 'null' end, 52 | [nullType] = encodeOthers 53 | } 54 | if undefinedType then 55 | ret[undefinedType] = encodeOthers 56 | end 57 | return ret 58 | end 59 | 60 | local others = { 61 | encodeBoolean = encodeBoolean, 62 | mergeOptions = mergeOptions, 63 | getEncoder = getEncoder 64 | } 65 | 66 | return others 67 | -------------------------------------------------------------------------------- /automation/include/json/encode/output.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local type = type 6 | local assert, error = assert, error 7 | local table_concat = require("table").concat 8 | local loadstring = loadstring or load 9 | 10 | local io = require("io") 11 | 12 | local setmetatable = setmetatable 13 | 14 | local output_utility = require("json.encode.output_utility") 15 | 16 | local _ENV = nil 17 | 18 | local tableCompositeCache = setmetatable({}, {__mode = 'v'}) 19 | 20 | local TABLE_VALUE_WRITER = [[ 21 | ret[#ret + 1] = %VALUE% 22 | ]] 23 | 24 | local TABLE_INNER_WRITER = "" 25 | 26 | --[[ 27 | nextValues can output a max of two values to throw into the data stream 28 | expected to be called until nil is first return value 29 | value separator should either be attached to v1 or in innerValue 30 | ]] 31 | local function defaultTableCompositeWriter(nextValues, beginValue, closeValue, innerValue, composite, encode, state) 32 | if type(nextValues) == 'string' then 33 | local fun = output_utility.prepareEncoder(defaultTableCompositeWriter, nextValues, innerValue, TABLE_VALUE_WRITER, TABLE_INNER_WRITER) 34 | local ret = {} 35 | fun(composite, ret, encode, state) 36 | return beginValue .. table_concat(ret, innerValue) .. closeValue 37 | end 38 | end 39 | 40 | -- no 'simple' as default action is just to return the value 41 | local function getDefault() 42 | return { composite = defaultTableCompositeWriter } 43 | end 44 | 45 | -- BEGIN IO-WRITER OUTPUT 46 | local IO_INNER_WRITER = [[ 47 | if %WRITE_INNER% then 48 | state.__outputFile:write(%INNER_VALUE%) 49 | end 50 | ]] 51 | local IO_VALUE_WRITER = [[ 52 | state.__outputFile:write(%VALUE%) 53 | ]] 54 | 55 | local function buildIoWriter(output) 56 | if not output then -- Default to stdout 57 | output = io.output() 58 | end 59 | local function ioWriter(nextValues, beginValue, closeValue, innerValue, composite, encode, state) 60 | -- HOOK OUTPUT STATE 61 | state.__outputFile = output 62 | if type(nextValues) == 'string' then 63 | local fun = output_utility.prepareEncoder(ioWriter, nextValues, innerValue, IO_VALUE_WRITER, IO_INNER_WRITER) 64 | local ret = {} 65 | output:write(beginValue) 66 | fun(composite, ret, encode, state) 67 | output:write(closeValue) 68 | return nil 69 | end 70 | end 71 | 72 | local function ioSimpleWriter(encoded) 73 | if encoded then 74 | output:write(encoded) 75 | end 76 | return nil 77 | end 78 | return { composite = ioWriter, simple = ioSimpleWriter } 79 | end 80 | local function getIoWriter(output) 81 | return function() 82 | return buildIoWriter(output) 83 | end 84 | end 85 | 86 | local output = { 87 | getDefault = getDefault, 88 | getIoWriter = getIoWriter 89 | } 90 | 91 | return output 92 | -------------------------------------------------------------------------------- /automation/include/json/encode/output_utility.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local setmetatable = setmetatable 6 | local assert, loadstring = assert, loadstring or load 7 | 8 | local _ENV = nil 9 | 10 | -- Key == weak, if main key goes away, then cache cleared 11 | local outputCache = setmetatable({}, {__mode = 'k'}) 12 | -- TODO: inner tables weak? 13 | 14 | local function buildFunction(nextValues, innerValue, valueWriter, innerWriter) 15 | local putInner = "" 16 | if innerValue and innerWriter then 17 | -- Prepare the lua-string representation of the separator to put in between values 18 | local formattedInnerValue = ("%q"):format(innerValue) 19 | -- Fill in the condition %WRITE_INNER% and the %INNER_VALUE% to actually write 20 | putInner = innerWriter:gsub("%%WRITE_INNER%%", "%%1"):gsub("%%INNER_VALUE%%", formattedInnerValue) 21 | end 22 | -- Template-in the value writer (if present) and its conditional argument 23 | local functionCode = nextValues:gsub("PUTINNER(%b())", putInner) 24 | -- %VALUE% is to be filled in by the value-to-write 25 | valueWriter = valueWriter:gsub("%%VALUE%%", "%%1") 26 | -- Template-in the value writer with its argument 27 | functionCode = functionCode:gsub("PUTVALUE(%b())", valueWriter) 28 | functionCode = [[ 29 | return function(composite, ret, encode, state) 30 | ]] .. functionCode .. [[ 31 | end 32 | ]] 33 | return assert(loadstring(functionCode))() 34 | end 35 | 36 | local function prepareEncoder(cacheKey, nextValues, innerValue, valueWriter, innerWriter) 37 | local cache = outputCache[cacheKey] 38 | if not cache then 39 | cache = {} 40 | outputCache[cacheKey] = cache 41 | end 42 | local fun = cache[nextValues] 43 | if not fun then 44 | fun = buildFunction(nextValues, innerValue, valueWriter, innerWriter) 45 | cache[nextValues] = fun 46 | end 47 | return fun 48 | end 49 | 50 | local output_utility = { 51 | prepareEncoder = prepareEncoder 52 | } 53 | 54 | return output_utility 55 | -------------------------------------------------------------------------------- /automation/include/json/encode/strings.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local string_char = require("string").char 6 | local pairs = pairs 7 | 8 | local jsonutil = require("json.util") 9 | local util_merge = jsonutil.merge 10 | 11 | local _ENV = nil 12 | 13 | local normalEncodingMap = { 14 | ['"'] = '\\"', 15 | ['\\'] = '\\\\', 16 | ['/'] = '\\/', 17 | ['\b'] = '\\b', 18 | ['\f'] = '\\f', 19 | ['\n'] = '\\n', 20 | ['\r'] = '\\r', 21 | ['\t'] = '\\t', 22 | ['\v'] = '\\v' -- not in official spec, on report, removing 23 | } 24 | 25 | local xEncodingMap = {} 26 | for char, encoded in pairs(normalEncodingMap) do 27 | xEncodingMap[char] = encoded 28 | end 29 | 30 | -- Pre-encode the control characters to speed up encoding... 31 | -- NOTE: UTF-8 may not work out right w/ JavaScript 32 | -- JavaScript uses 2 bytes after a \u... yet UTF-8 is a 33 | -- byte-stream encoding, not pairs of bytes (it does encode 34 | -- some letters > 1 byte, but base case is 1) 35 | for i = 0, 255 do 36 | local c = string_char(i) 37 | if c:match('[%z\1-\031\128-\255]') and not normalEncodingMap[c] then 38 | -- WARN: UTF8 specializes values >= 0x80 as parts of sequences... 39 | -- without \x encoding, do not allow encoding > 7F 40 | normalEncodingMap[c] = ('\\u%.4X'):format(i) 41 | xEncodingMap[c] = ('\\x%.2X'):format(i) 42 | end 43 | end 44 | 45 | local defaultOptions = { 46 | xEncode = false, -- Encode single-bytes as \xXX 47 | processor = nil, -- Simple processor for the string prior to quoting 48 | -- / is not required to be quoted but it helps with certain decoding 49 | -- Required encoded characters, " \, and 00-1F (0 - 31) 50 | encodeSet = '\\"/%z\1-\031', 51 | encodeSetAppend = nil -- Chars to append to the default set 52 | } 53 | 54 | local modeOptions = {} 55 | 56 | local function mergeOptions(options, mode) 57 | jsonutil.doOptionMerge(options, false, 'strings', defaultOptions, mode and modeOptions[mode]) 58 | end 59 | 60 | local function getEncoder(options) 61 | options = options and util_merge({}, defaultOptions, options) or defaultOptions 62 | local encodeSet = options.encodeSet 63 | if options.encodeSetAppend then 64 | encodeSet = encodeSet .. options.encodeSetAppend 65 | end 66 | local encodingMap = options.xEncode and xEncodingMap or normalEncodingMap 67 | local encodeString 68 | if options.processor then 69 | local processor = options.processor 70 | encodeString = function(s, state) 71 | return '"' .. processor(s:gsub('[' .. encodeSet .. ']', encodingMap)) .. '"' 72 | end 73 | else 74 | encodeString = function(s, state) 75 | return '"' .. s:gsub('[' .. encodeSet .. ']', encodingMap) .. '"' 76 | end 77 | end 78 | return { 79 | string = encodeString 80 | } 81 | end 82 | 83 | local strings = { 84 | mergeOptions = mergeOptions, 85 | getEncoder = getEncoder 86 | } 87 | 88 | return strings 89 | -------------------------------------------------------------------------------- /automation/include/json/util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Licensed according to the included 'LICENSE' document 3 | Author: Thomas Harning Jr 4 | ]] 5 | local type = type 6 | local print = print 7 | local tostring = tostring 8 | local pairs = pairs 9 | local getmetatable, setmetatable = getmetatable, setmetatable 10 | local select = select 11 | 12 | local _ENV = nil 13 | 14 | local function foreach(tab, func) 15 | for k, v in pairs(tab) do 16 | func(k,v) 17 | end 18 | end 19 | local function printValue(tab, name) 20 | local parsed = {} 21 | local function doPrint(key, value, space) 22 | space = space or '' 23 | if type(value) == 'table' then 24 | if parsed[value] then 25 | print(space .. key .. '= <' .. parsed[value] .. '>') 26 | else 27 | parsed[value] = key 28 | print(space .. key .. '= {') 29 | space = space .. ' ' 30 | foreach(value, function(key, value) doPrint(key, value, space) end) 31 | end 32 | else 33 | if type(value) == 'string' then 34 | value = '[[' .. tostring(value) .. ']]' 35 | end 36 | print(space .. key .. '=' .. tostring(value)) 37 | end 38 | end 39 | doPrint(name, tab) 40 | end 41 | 42 | local function clone(t) 43 | local ret = {} 44 | for k,v in pairs(t) do 45 | ret[k] = v 46 | end 47 | return ret 48 | end 49 | 50 | local function inner_merge(t, remaining, from, ...) 51 | if remaining == 0 then 52 | return t 53 | end 54 | if from then 55 | for k,v in pairs(from) do 56 | t[k] = v 57 | end 58 | end 59 | return inner_merge(t, remaining - 1, ...) 60 | end 61 | 62 | --[[* 63 | Shallow-merges tables in order onto the first table. 64 | 65 | @param t table to merge entries onto 66 | @param ... sequence of 0 or more tables to merge onto 't' 67 | 68 | @returns table 't' from input 69 | ]] 70 | local function merge(t, ...) 71 | return inner_merge(t, select('#', ...), ...) 72 | end 73 | 74 | -- Function to insert nulls into the JSON stream 75 | local function null() 76 | return null 77 | end 78 | 79 | -- Marker for 'undefined' values 80 | local function undefined() 81 | return undefined 82 | end 83 | 84 | local ArrayMT = {} 85 | 86 | --[[ 87 | Return's true if the metatable marks it as an array.. 88 | Or false if it has no array component at all 89 | Otherwise nil to get the normal detection component working 90 | ]] 91 | local function IsArray(value) 92 | if type(value) ~= 'table' then return false end 93 | local meta = getmetatable(value) 94 | local ret = meta == ArrayMT or (meta ~= nil and meta.__is_luajson_array) 95 | if not ret then 96 | if #value == 0 then return false end 97 | else 98 | return ret 99 | end 100 | end 101 | local function InitArray(array) 102 | setmetatable(array, ArrayMT) 103 | return array 104 | end 105 | 106 | local CallMT = {} 107 | 108 | local function isCall(value) 109 | return CallMT == getmetatable(value) 110 | end 111 | 112 | local function buildCall(name, ...) 113 | local callData = { 114 | name = name, 115 | parameters = {n = select('#', ...), ...} 116 | } 117 | return setmetatable(callData, CallMT) 118 | end 119 | 120 | local function decodeCall(callData) 121 | if not isCall(callData) then return nil end 122 | return callData.name, callData.parameters 123 | end 124 | 125 | local function doOptionMerge(options, nested, name, defaultOptions, modeOptions) 126 | if nested then 127 | modeOptions = modeOptions and modeOptions[name] 128 | defaultOptions = defaultOptions and defaultOptions[name] 129 | end 130 | options[name] = merge( 131 | {}, 132 | defaultOptions, 133 | modeOptions, 134 | options[name] 135 | ) 136 | end 137 | 138 | local json_util = { 139 | printValue = printValue, 140 | clone = clone, 141 | merge = merge, 142 | null = null, 143 | undefined = undefined, 144 | IsArray = IsArray, 145 | InitArray = InitArray, 146 | isCall = isCall, 147 | buildCall = buildCall, 148 | decodeCall = decodeCall, 149 | doOptionMerge = doOptionMerge 150 | } 151 | 152 | return json_util 153 | -------------------------------------------------------------------------------- /automation/include/luajit-request/init.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | LuaJIT-Request 3 | Lucien Greathouse 4 | Wrapper for LuaJIT-cURL for easy HTTP(S) requests. 5 | 6 | Copyright (c) 2016 Lucien Greathouse 7 | 8 | This software is provided 'as-is', without any express 9 | or implied warranty. In no event will the authors be held 10 | liable for any damages arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, andto alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 21 | 2. Altered source versions must be plainly marked as such, and must 22 | not be misrepresented as being the original software. 23 | 24 | 3. This notice may not be removed or altered from any source distribution. 25 | ]] 26 | 27 | local path = (...):gsub("%.init$", ""):match("%.?(.-)$") .. "." 28 | 29 | local ffi = require("ffi") 30 | local curl = require(path .. "luajit-curl") 31 | local request 32 | 33 | local function url_encode(str) 34 | if (str) then 35 | str = str:gsub("\n", "\r\n") 36 | str = str:gsub("([^%w %-%_%.%~])", function(c) 37 | return string.format ("%%%02X", string.byte(c)) 38 | end) 39 | str = str:gsub(" ", "%%20") 40 | end 41 | return str 42 | end 43 | 44 | local function cookie_encode(str, name) 45 | str = str:gsub("[,;%s]", "") 46 | 47 | if (name) then 48 | str = str:gsub("=", "") 49 | end 50 | 51 | return str 52 | end 53 | 54 | local auth_map = { 55 | BASIC = ffi.cast("long", curl.CURLAUTH_BASIC), 56 | DIGEST = ffi.cast("long", curl.CURLAUTH_DIGEST), 57 | NEGOTIATE = ffi.cast("long", curl.CURLAUTH_NEGOTIATE) 58 | } 59 | 60 | local errors = { 61 | unknown = 0, 62 | timeout = 1, 63 | connect = 2, 64 | resolve_host = 3 65 | } 66 | 67 | local code_map = { 68 | [curl.CURLE_OPERATION_TIMEDOUT] = { 69 | errors.timeout, "Connection timed out" 70 | }, 71 | [curl.CURLE_COULDNT_RESOLVE_HOST] = { 72 | errors.resolve_host, "Couldn't resolve host" 73 | }, 74 | [curl.CURLE_COULDNT_CONNECT] = { 75 | errors.connect, "Couldn't connect to host" 76 | } 77 | } 78 | 79 | request = { 80 | error = errors, 81 | 82 | version = "2.4.0", 83 | version_major = 2, 84 | version_minor = 4, 85 | version_patch = 0, 86 | 87 | --[[ 88 | Send an HTTP(S) request to the URL at 'url' using the HTTP method 'method'. 89 | Use the 'args' parameter to optionally configure the request: 90 | - method: HTTP method to use. Defaults to "GET", but can be any HTTP verb like "POST" or "PUT" 91 | - headers: Dictionary of additional HTTP headers to send with request 92 | - data: Dictionary or string to send as request body 93 | - cookies: Dictionary table of cookies to send 94 | - timeout: How long to wait for the connection to be made before giving up 95 | - allow_redirects: Whether or not to allow redirection. Defaults to true 96 | - body_stream_callback: A method to call with each piece of the response body. 97 | - header_stream_callback: A method to call with each piece of the resulting header. 98 | - transfer_info_callback: A method to call with transfer progress data. 99 | - auth_type: Authentication method to use. Defaults to "none", but can also be "basic", "digest" or "negotiate" 100 | - username: A username to use with authentication. 'auth_type' must also be specified. 101 | - password: A password to use with authentication. 'auth_type' must also be specified. 102 | - files: A dictionary of file names to their paths on disk to upload via stream. 103 | 104 | If both body_stream_callback and header_stream_callback are defined, a boolean true will be returned instead of the following object. 105 | 106 | The return object is a dictionary with the following members: 107 | - code: The HTTP status code the response gave. Will not exist if header_stream_callback is defined above. 108 | - body: The body of the response. Will not exist if body_stream_callback is defined above. 109 | - headers: A dictionary of headers and their values. Will not exist if header_stream_callback is defined above. 110 | - headers_raw: A raw string containing the actual headers the server sent back. Will not exist if header_stream_callback is defined above. 111 | - set_cookies: A dictionary of cookies given by the "Set-Cookie" header from the server. Will not exist if the server did not set any cookies. 112 | 113 | If an error occured, false will be returned along with a curl error code and a message. 114 | ]] 115 | send = function(url, args) 116 | local handle = curl.curl_easy_init() 117 | local header_chunk 118 | local out_buffer 119 | local headers_buffer 120 | args = args or {} 121 | 122 | local callbacks = {} 123 | local gc_handles = {} 124 | 125 | curl.curl_easy_setopt(handle, curl.CURLOPT_URL, url) 126 | curl.curl_easy_setopt(handle, curl.CURLOPT_SSL_VERIFYPEER, 1) 127 | curl.curl_easy_setopt(handle, curl.CURLOPT_SSL_VERIFYHOST, 2) 128 | 129 | if (args.data and type(args.data) ~= "table") then 130 | local default_content_type = "application/octet-stream" 131 | if (not args.headers) then 132 | args.headers = { 133 | ["content-type"] = default_content_type 134 | } 135 | else 136 | local has_content_type = false 137 | for header_name, _ in pairs(args.headers) do 138 | if header_name:lower() == "content-type" then 139 | has_content_type = true 140 | break 141 | end 142 | end 143 | if not has_content_type then 144 | args.headers["content-type"] = default_content_type 145 | end 146 | end 147 | end 148 | 149 | if (args.method) then 150 | local method = string.upper(tostring(args.method)) 151 | 152 | if (method == "GET") then 153 | curl.curl_easy_setopt(handle, curl.CURLOPT_HTTPGET, 1) 154 | elseif (method == "POST") then 155 | curl.curl_easy_setopt(handle, curl.CURLOPT_POST, 1) 156 | args.data = args.data or "" -- https://github.com/curl/curl/issues/1625#issuecomment-312456910 157 | else 158 | curl.curl_easy_setopt(handle, curl.CURLOPT_CUSTOMREQUEST, method) 159 | end 160 | end 161 | 162 | if (args.headers) then 163 | for key, value in pairs(args.headers) do 164 | header_chunk = curl.curl_slist_append(header_chunk, tostring(key) .. ":" .. tostring(value)) 165 | end 166 | 167 | curl.curl_easy_setopt(handle, curl.CURLOPT_HTTPHEADER, header_chunk) 168 | end 169 | 170 | if (args.auth_type) then 171 | local auth = string.upper(tostring(args.auth_type)) 172 | 173 | if (auth_map[auth]) then 174 | curl.curl_easy_setopt(handle, curl.CURLOPT_HTTPAUTH, auth_map[auth]) 175 | curl.curl_easy_setopt(handle, curl.CURLOPT_USERNAME, tostring(args.username)) 176 | curl.curl_easy_setopt(handle, curl.CURLOPT_PASSWORD, tostring(args.password or "")) 177 | elseif (auth ~= "NONE") then 178 | error("Unsupported authentication type '" .. auth .. "'") 179 | end 180 | end 181 | 182 | if (args.body_stream_callback) then 183 | local callback = ffi.cast("curl_callback", function(data, size, nmeb, user) 184 | args.body_stream_callback(ffi.string(data, size * nmeb)) 185 | return size * nmeb 186 | end) 187 | 188 | table.insert(callbacks, callback) 189 | 190 | curl.curl_easy_setopt(handle, curl.CURLOPT_WRITEFUNCTION, callback) 191 | else 192 | out_buffer = {} 193 | 194 | local callback = ffi.cast("curl_callback", function(data, size, nmeb, user) 195 | table.insert(out_buffer, ffi.string(data, size * nmeb)) 196 | return size * nmeb 197 | end) 198 | 199 | table.insert(callbacks, callback) 200 | 201 | curl.curl_easy_setopt(handle, curl.CURLOPT_WRITEFUNCTION, callback) 202 | end 203 | 204 | if (args.header_stream_callback) then 205 | local callback = ffi.cast("curl_callback", function(data, size, nmeb, user) 206 | args.header_stream_callback(ffi.string(data, size * nmeb)) 207 | return size * nmeb 208 | end) 209 | 210 | table.insert(callbacks, callback) 211 | 212 | curl.curl_easy_setopt(handle, curl.CURLOPT_HEADERFUNCTION, callback) 213 | else 214 | headers_buffer = {} 215 | 216 | local callback = ffi.cast("curl_callback", function(data, size, nmeb, user) 217 | table.insert(headers_buffer, ffi.string(data, size * nmeb)) 218 | return size * nmeb 219 | end) 220 | 221 | table.insert(callbacks, callback) 222 | 223 | curl.curl_easy_setopt(handle, curl.CURLOPT_HEADERFUNCTION, callback) 224 | end 225 | 226 | if (args.transfer_info_callback) then 227 | local callback = ffi.cast("curl_xferinfo_callback", function(client, dltotal, dlnow, ultotal, ulnow) 228 | args.transfer_info_callback(tonumber(dltotal), tonumber(dlnow), tonumber(ultotal), tonumber(ulnow)) 229 | return 0 230 | end) 231 | 232 | table.insert(callbacks, callback) 233 | 234 | curl.curl_easy_setopt(handle, curl.CURLOPT_NOPROGRESS, 0) 235 | curl.curl_easy_setopt(handle, curl.CURLOPT_XFERINFOFUNCTION, callback) 236 | end 237 | 238 | if (args.follow_redirects == nil) then 239 | curl.curl_easy_setopt(handle, curl.CURLOPT_FOLLOWLOCATION, true) 240 | else 241 | curl.curl_easy_setopt(handle, curl.CURLOPT_FOLLOWLOCATION, not not args.follow_redirects) 242 | end 243 | 244 | if (args.data) then 245 | if (type(args.data) == "table") then 246 | local buffer = {} 247 | for key, value in pairs(args.data) do 248 | table.insert(buffer, ("%s=%s"):format(url_encode(key), url_encode(value))) 249 | end 250 | 251 | curl.curl_easy_setopt(handle, curl.CURLOPT_POSTFIELDS, table.concat(buffer, "&")) 252 | else 253 | curl.curl_easy_setopt(handle, curl.CURLOPT_POSTFIELDS, tostring(args.data)) 254 | end 255 | end 256 | 257 | local post 258 | if (args.files) then 259 | post = ffi.new("struct curl_httppost*[1]") 260 | local lastptr = ffi.new("struct curl_httppost*[1]") 261 | 262 | for key, value in pairs(args.files) do 263 | local file = ffi.new("char[?]", #value, value) 264 | 265 | table.insert(gc_handles, file) 266 | 267 | local res = curl.curl_formadd( 268 | post, lastptr, 269 | ffi.new("int", curl.CURLFORM_COPYNAME), key, 270 | ffi.new("int", curl.CURLFORM_FILE), file, 271 | ffi.new("int", curl.CURLFORM_END) 272 | ) 273 | end 274 | 275 | curl.curl_easy_setopt(handle, curl.CURLOPT_HTTPPOST, post[0]) 276 | end 277 | 278 | -- Enable the cookie engine 279 | curl.curl_easy_setopt(handle, curl.CURLOPT_COOKIEFILE, "") 280 | 281 | if (args.cookies) then 282 | local cookie_out 283 | 284 | if (type(args.cookies) == "table") then 285 | local buffer = {} 286 | for key, value in pairs(args.cookies) do 287 | table.insert(buffer, ("%s=%s"):format(cookie_encode(key, true), cookie_encode(value))) 288 | end 289 | 290 | cookie_out = table.concat(buffer, "; ") 291 | else 292 | cookie_out = tostring(args.cookies) 293 | end 294 | 295 | curl.curl_easy_setopt(handle, curl.CURLOPT_COOKIE, cookie_out) 296 | end 297 | 298 | if (tonumber(args.timeout)) then 299 | curl.curl_easy_setopt(handle, curl.CURLOPT_CONNECTTIMEOUT, tonumber(args.timeout)) 300 | end 301 | 302 | local code = curl.curl_easy_perform(handle) 303 | 304 | if (code ~= curl.CURLE_OK) then 305 | local num = tonumber(code) 306 | 307 | if (code_map[num]) then 308 | return false, code_map[num][1], code_map[num][2] 309 | end 310 | 311 | return false, request.error.unknown, "Unknown error", num 312 | end 313 | 314 | local out 315 | 316 | if (out_buffer or headers_buffer) then 317 | local headers, status, parsed_headers, raw_cookies, set_cookies 318 | 319 | if (headers_buffer) then 320 | -- In case we got multiple responses (e.g. 100 - Continue or 302 Redirects) 321 | -- we want to only return the last response 322 | local start_index = 1 323 | for i, resp_line in ipairs(headers_buffer) do 324 | if resp_line:match("^HTTP/(.-)%s+(%d+)%s+(.+)\r\n$") then 325 | start_index = i 326 | end 327 | end 328 | local last_request_headers = {} 329 | for i = start_index, #headers_buffer do 330 | table.insert(last_request_headers, headers_buffer[i]) 331 | end 332 | headers = table.concat(last_request_headers) 333 | status = tonumber(headers:match("%s+(%d+)%s+")) 334 | 335 | parsed_headers = {} 336 | 337 | for key, value in headers:gmatch("\n([^:]+): *([^\r\n]*)") do 338 | parsed_headers[key] = value 339 | end 340 | end 341 | 342 | local cookielist = ffi.new("struct curl_slist*[1]") 343 | curl.curl_easy_getinfo(handle, curl.CURLINFO_COOKIELIST, cookielist) 344 | if cookielist[0] ~= nil then 345 | raw_cookies, set_cookies = {}, {} 346 | local cookielist = ffi.gc(cookielist[0], curl.curl_slist_free_all) 347 | local cookie = cookielist 348 | 349 | repeat 350 | local raw = ffi.string(cookie[0].data) 351 | table.insert(raw_cookies, raw) 352 | 353 | local domain, subdomains, path, secure, expiration, name, value = raw:match("^(.-)\t(.-)\t(.-)\t(.-)\t(.-)\t(.-)\t(.*)$") 354 | set_cookies[name] = value 355 | cookie = cookie[0].next 356 | until cookie == nil 357 | end 358 | 359 | out = { 360 | body = table.concat(out_buffer), 361 | headers = parsed_headers, 362 | raw_cookies = raw_cookies, 363 | set_cookies = set_cookies, 364 | code = status, 365 | raw_headers = headers 366 | } 367 | else 368 | out = true 369 | end 370 | 371 | curl.curl_easy_cleanup(handle) 372 | curl.curl_slist_free_all(header_chunk) 373 | 374 | if (post) then 375 | curl.curl_formfree(post[0]) 376 | end 377 | gc_handles = {} 378 | 379 | for i, v in ipairs(callbacks) do 380 | v:free() 381 | end 382 | 383 | return out 384 | end, 385 | 386 | init = function() 387 | curl.curl_global_init(curl.CURL_GLOBAL_ALL) 388 | end, 389 | 390 | close = function() 391 | curl.curl_global_cleanup() 392 | end 393 | } 394 | 395 | request.init() 396 | 397 | return request 398 | -------------------------------------------------------------------------------- /automation/include/luajit-request/luajit-curl.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | LuaJIT-cURL 3 | Lucien Greathouse 4 | LuaJIT FFI cURL binding aimed at cURL version 7.38.0. 5 | 6 | Copyright (c) 2014 lucien Greathouse 7 | 8 | This software is provided 'as-is', without any express 9 | or implied warranty. In no event will the authors be held 10 | liable for any damages arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, andto alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 21 | 2. Altered source versions must be plainly marked as such, and must 22 | not be misrepresented as being the original software. 23 | 24 | 3. This notice may not be removed or altered from any source distribution. 25 | ]] 26 | 27 | local ffi = require("ffi") 28 | local curl = ffi.load("libcurl") 29 | 30 | if (jit.os == "Windows") then 31 | --Windows! 32 | ffi.cdef([[ 33 | //windows layering 34 | enum { 35 | INVALID_SOCKET = ~0, 36 | SOCKET_BAD = ~0 37 | }; 38 | ]]) 39 | else 40 | --Not Windows! 41 | ffi.cdef([[ 42 | typedef int socket_t; 43 | 44 | enum { 45 | SOCKET_BAD = -1 46 | }; 47 | ]]) 48 | end 49 | 50 | ffi.cdef([[ 51 | typedef int64_t time_t; 52 | typedef unsigned int size_t; 53 | 54 | typedef size_t (*curl_callback)(char *data, size_t size, size_t nmeb, void *userdata); 55 | ]]) 56 | 57 | --curlver.h 58 | ffi.cdef([[ 59 | /*************************************************************************** 60 | * _ _ ____ _ 61 | * Project ___| | | | _ \| | 62 | * / __| | | | |_) | | 63 | * | (__| |_| | _ <| |___ 64 | * \___|\___/|_| \_\_____| 65 | * 66 | * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. 67 | * 68 | * This software is licensed as described in the file COPYING, which 69 | * you should have received as part of this distribution. The terms 70 | * are also available at http://curl.haxx.se/docs/copyright.html. 71 | * 72 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell 73 | * copies of the Software, and permit persons to whom the Software is 74 | * furnished to do so, under the terms of the COPYING file. 75 | * 76 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 77 | * KIND, either express or implied. 78 | * 79 | ***************************************************************************/ 80 | 81 | enum { 82 | LIBCURL_VERSION_MAJOR = 7, 83 | LIBCURL_VERSION_MINOR = 38, 84 | LIBCURL_VERSION_PATCH = 0, 85 | LIBCURL_VERSION_NUM = 0x072600 86 | } 87 | ]]) 88 | 89 | --cURL's type aliasing, built around curlbuild.h 90 | ffi.cdef([[ 91 | typedef int64_t curl_off_t; 92 | ]]) 93 | 94 | --Constants 95 | ffi.cdef([[ 96 | enum { 97 | CURL_GLOBAL_SSL = (1<<0), 98 | CURL_GLOBAL_WIN32 = (1<<1), 99 | CURL_GLOBAL_ALL = (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32), 100 | CURL_GLOBAL_NOTHING = 0, 101 | CURL_GLOBAL_DEFAULT = CURL_GLOBAL_ALL, 102 | CURL_GLOBAL_ACK_EINTR = (1<<2) 103 | }; 104 | 105 | enum { 106 | CURLAUTH_NONE = 0, 107 | CURLAUTH_BASIC = 1, 108 | CURLAUTH_DIGEST = 1<<1, 109 | CURLAUTH_NEGOTIATE = 1<<2 110 | }; 111 | ]]) 112 | 113 | ffi.cdef([[ 114 | typedef void CURL; 115 | typedef int curl_socket_t; 116 | typedef struct curl_httppost { 117 | struct curl_httppost *next; 118 | char *name; 119 | long namelength; 120 | char *contents; 121 | long contentslength; 122 | char *buffer; 123 | long bufferlength; 124 | char *contenttype; 125 | struct curl_slist* contentheader; 126 | struct curl_httppost *more; 127 | long flags; 128 | char *showfilename; 129 | void *userp; 130 | }; 131 | typedef int (*curl_progress_callback)(void *clientp, 132 | double dltotal, 133 | double dlnow, 134 | double ultotal, 135 | double ulnow); 136 | typedef int (*curl_xferinfo_callback)(void *clientp, 137 | curl_off_t dltotal, 138 | curl_off_t dlnow, 139 | curl_off_t ultotal, 140 | curl_off_t ulnow); 141 | typedef size_t (*curl_write_callback)(char *buffer, 142 | size_t size, 143 | size_t nitems, 144 | void *outstream); 145 | typedef enum { 146 | CURLFILETYPE_FILE = 0, 147 | CURLFILETYPE_DIRECTORY, 148 | CURLFILETYPE_SYMLINK, 149 | CURLFILETYPE_DEVICE_BLOCK, 150 | CURLFILETYPE_DEVICE_CHAR, 151 | CURLFILETYPE_NAMEDPIPE, 152 | CURLFILETYPE_SOCKET, 153 | CURLFILETYPE_DOOR, 154 | CURLFILETYPE_UNKNOWN 155 | } curlfiletype; 156 | struct curl_fileinfo { 157 | char *filename; 158 | curlfiletype filetype; 159 | time_t time; 160 | unsigned int perm; 161 | int uid; 162 | int gid; 163 | curl_off_t size; 164 | long int hardlinks; 165 | struct { 166 | char *time; 167 | char *perm; 168 | char *user; 169 | char *group; 170 | char *target; 171 | } strings; 172 | unsigned int flags; 173 | char * b_data; 174 | size_t b_size; 175 | size_t b_used; 176 | }; 177 | typedef long (*curl_chunk_bgn_callback)(const void *transfer_info, 178 | void *ptr, 179 | int remains); 180 | typedef long (*curl_chunk_end_callback)(void *ptr); 181 | typedef int (*curl_fnmatch_callback)(void *ptr, 182 | const char *pattern, 183 | const char *string); 184 | typedef int (*curl_seek_callback)(void *instream, 185 | curl_off_t offset, 186 | int origin); 187 | typedef size_t (*curl_read_callback)(char *buffer, 188 | size_t size, 189 | size_t nitems, 190 | void *instream); 191 | typedef enum { 192 | CURLSOCKTYPE_IPCXN, 193 | CURLSOCKTYPE_ACCEPT, 194 | CURLSOCKTYPE_LAST 195 | } curlsocktype; 196 | typedef int (*curl_sockopt_callback)(void *clientp, 197 | curl_socket_t curlfd, 198 | curlsocktype purpose); 199 | struct sockaddr { 200 | uint8_t sa_family; 201 | char sa_data[14]; 202 | }; 203 | struct curl_sockaddr { 204 | int family; 205 | int socktype; 206 | int protocol; 207 | unsigned int addrlen; 208 | struct sockaddr addr; 209 | }; 210 | typedef curl_socket_t 211 | (*curl_opensocket_callback)(void *clientp, 212 | curlsocktype purpose, 213 | struct curl_sockaddr *address); 214 | typedef int 215 | (*curl_closesocket_callback)(void *clientp, curl_socket_t item); 216 | typedef enum { 217 | CURLIOE_OK, 218 | CURLIOE_UNKNOWNCMD, 219 | CURLIOE_FAILRESTART, 220 | CURLIOE_LAST 221 | } curlioerr; 222 | typedef enum { 223 | CURLIOCMD_NOP, 224 | CURLIOCMD_RESTARTREAD, 225 | CURLIOCMD_LAST 226 | } curliocmd; 227 | typedef curlioerr (*curl_ioctl_callback)(CURL *handle, 228 | int cmd, 229 | void *clientp); 230 | typedef void *(*curl_malloc_callback)(size_t size); 231 | typedef void (*curl_free_callback)(void *ptr); 232 | typedef void *(*curl_realloc_callback)(void *ptr, size_t size); 233 | typedef char *(*curl_strdup_callback)(const char *str); 234 | typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size); 235 | typedef enum { 236 | CURLINFO_TEXT = 0, 237 | CURLINFO_HEADER_IN, 238 | CURLINFO_HEADER_OUT, 239 | CURLINFO_DATA_IN, 240 | CURLINFO_DATA_OUT, 241 | CURLINFO_SSL_DATA_IN, 242 | CURLINFO_SSL_DATA_OUT, 243 | CURLINFO_END 244 | } curl_infotype; 245 | typedef int (*curl_debug_callback) 246 | (CURL *handle, 247 | curl_infotype type, 248 | char *data, 249 | size_t size, 250 | void *userptr); 251 | typedef enum { 252 | CURLE_OK = 0, 253 | CURLE_UNSUPPORTED_PROTOCOL, 254 | CURLE_FAILED_INIT, 255 | CURLE_URL_MALFORMAT, 256 | CURLE_NOT_BUILT_IN, 257 | CURLE_COULDNT_RESOLVE_PROXY, 258 | CURLE_COULDNT_RESOLVE_HOST, 259 | CURLE_COULDNT_CONNECT, 260 | CURLE_FTP_WEIRD_SERVER_REPLY, 261 | CURLE_REMOTE_ACCESS_DENIED, 262 | CURLE_FTP_ACCEPT_FAILED, 263 | CURLE_FTP_WEIRD_PASS_REPLY, 264 | CURLE_FTP_ACCEPT_TIMEOUT, 265 | CURLE_FTP_WEIRD_PASV_REPLY, 266 | CURLE_FTP_WEIRD_227_FORMAT, 267 | CURLE_FTP_CANT_GET_HOST, 268 | CURLE_HTTP2, 269 | CURLE_FTP_COULDNT_SET_TYPE, 270 | CURLE_PARTIAL_FILE, 271 | CURLE_FTP_COULDNT_RETR_FILE, 272 | CURLE_OBSOLETE20, 273 | CURLE_QUOTE_ERROR, 274 | CURLE_HTTP_RETURNED_ERROR, 275 | CURLE_WRITE_ERROR, 276 | CURLE_OBSOLETE24, 277 | CURLE_UPLOAD_FAILED, 278 | CURLE_READ_ERROR, 279 | CURLE_OUT_OF_MEMORY, 280 | CURLE_OPERATION_TIMEDOUT, 281 | CURLE_OBSOLETE29, 282 | CURLE_FTP_PORT_FAILED, 283 | CURLE_FTP_COULDNT_USE_REST, 284 | CURLE_OBSOLETE32, 285 | CURLE_RANGE_ERROR, 286 | CURLE_HTTP_POST_ERROR, 287 | CURLE_SSL_CONNECT_ERROR, 288 | CURLE_BAD_DOWNLOAD_RESUME, 289 | CURLE_FILE_COULDNT_READ_FILE, 290 | CURLE_LDAP_CANNOT_BIND, 291 | CURLE_LDAP_SEARCH_FAILED, 292 | CURLE_OBSOLETE40, 293 | CURLE_FUNCTION_NOT_FOUND, 294 | CURLE_ABORTED_BY_CALLBACK, 295 | CURLE_BAD_FUNCTION_ARGUMENT, 296 | CURLE_OBSOLETE44, 297 | CURLE_INTERFACE_FAILED, 298 | CURLE_OBSOLETE46, 299 | CURLE_TOO_MANY_REDIRECTS , 300 | CURLE_UNKNOWN_OPTION, 301 | CURLE_TELNET_OPTION_SYNTAX , 302 | CURLE_OBSOLETE50, 303 | CURLE_PEER_FAILED_VERIFICATION, 304 | CURLE_GOT_NOTHING, 305 | CURLE_SSL_ENGINE_NOTFOUND, 306 | CURLE_SSL_ENGINE_SETFAILED, 307 | CURLE_SEND_ERROR, 308 | CURLE_RECV_ERROR, 309 | CURLE_OBSOLETE57, 310 | CURLE_SSL_CERTPROBLEM, 311 | CURLE_SSL_CIPHER, 312 | CURLE_SSL_CACERT, 313 | CURLE_BAD_CONTENT_ENCODING, 314 | CURLE_LDAP_INVALID_URL, 315 | CURLE_FILESIZE_EXCEEDED, 316 | CURLE_USE_SSL_FAILED, 317 | CURLE_SEND_FAIL_REWIND, 318 | CURLE_SSL_ENGINE_INITFAILED, 319 | CURLE_LOGIN_DENIED, 320 | CURLE_TFTP_NOTFOUND, 321 | CURLE_TFTP_PERM, 322 | CURLE_REMOTE_DISK_FULL, 323 | CURLE_TFTP_ILLEGAL, 324 | CURLE_TFTP_UNKNOWNID, 325 | CURLE_REMOTE_FILE_EXISTS, 326 | CURLE_TFTP_NOSUCHUSER, 327 | CURLE_CONV_FAILED, 328 | CURLE_CONV_REQD, 329 | CURLE_SSL_CACERT_BADFILE, 330 | CURLE_REMOTE_FILE_NOT_FOUND, 331 | CURLE_SSH, 332 | CURLE_SSL_SHUTDOWN_FAILED, 333 | CURLE_AGAIN, 334 | CURLE_SSL_CRL_BADFILE, 335 | CURLE_SSL_ISSUER_ERROR, 336 | CURLE_FTP_PRET_FAILED, 337 | CURLE_RTSP_CSEQ_ERROR, 338 | CURLE_RTSP_SESSION_ERROR, 339 | CURLE_FTP_BAD_FILE_LIST, 340 | CURLE_CHUNK_FAILED, 341 | CURLE_NO_CONNECTION_AVAILABLE, 342 | CURL_LAST 343 | } CURLcode; 344 | typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length); 345 | typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, 346 | void *ssl_ctx, 347 | void *userptr); 348 | typedef enum { 349 | CURLPROXY_HTTP = 0, 350 | CURLPROXY_HTTP_1_0 = 1, 351 | CURLPROXY_SOCKS4 = 4, 352 | CURLPROXY_SOCKS5 = 5, 353 | CURLPROXY_SOCKS4A = 6, 354 | CURLPROXY_SOCKS5_HOSTNAME = 7 355 | } curl_proxytype; 356 | enum curl_khtype { 357 | CURLKHTYPE_UNKNOWN, 358 | CURLKHTYPE_RSA1, 359 | CURLKHTYPE_RSA, 360 | CURLKHTYPE_DSS 361 | }; 362 | struct curl_khkey { 363 | const char *key; 364 | size_t len; 365 | enum curl_khtype keytype; 366 | }; 367 | enum curl_khstat { 368 | CURLKHSTAT_FINE_ADD_TO_FILE, 369 | CURLKHSTAT_FINE, 370 | CURLKHSTAT_REJECT, 371 | CURLKHSTAT_DEFER, 372 | CURLKHSTAT_LAST 373 | }; 374 | enum curl_khmatch { 375 | CURLKHMATCH_OK, 376 | CURLKHMATCH_MISMATCH, 377 | CURLKHMATCH_MISSING, 378 | CURLKHMATCH_LAST 379 | }; 380 | typedef int 381 | (*curl_sshkeycallback) (CURL *easy, 382 | const struct curl_khkey *knownkey, 383 | const struct curl_khkey *foundkey, 384 | enum curl_khmatch, 385 | void *clientp); 386 | typedef enum { 387 | CURLUSESSL_NONE, 388 | CURLUSESSL_TRY, 389 | CURLUSESSL_CONTROL, 390 | CURLUSESSL_ALL, 391 | CURLUSESSL_LAST 392 | } curl_usessl; 393 | typedef enum { 394 | CURLFTPSSL_CCC_NONE, 395 | CURLFTPSSL_CCC_PASSIVE, 396 | CURLFTPSSL_CCC_ACTIVE, 397 | CURLFTPSSL_CCC_LAST 398 | } curl_ftpccc; 399 | typedef enum { 400 | CURLFTPAUTH_DEFAULT, 401 | CURLFTPAUTH_SSL, 402 | CURLFTPAUTH_TLS, 403 | CURLFTPAUTH_LAST 404 | } curl_ftpauth; 405 | typedef enum { 406 | CURLFTP_CREATE_DIR_NONE, 407 | CURLFTP_CREATE_DIR, 408 | CURLFTP_CREATE_DIR_RETRY, 409 | CURLFTP_CREATE_DIR_LAST 410 | } curl_ftpcreatedir; 411 | typedef enum { 412 | CURLFTPMETHOD_DEFAULT, 413 | CURLFTPMETHOD_MULTICWD, 414 | CURLFTPMETHOD_NOCWD, 415 | CURLFTPMETHOD_SINGLECWD, 416 | CURLFTPMETHOD_LAST 417 | } curl_ftpmethod; 418 | typedef enum { 419 | CURLOPT_WRITEDATA = 10000 + 1, 420 | CURLOPT_URL = 10000 + 2, 421 | CURLOPT_PORT = 0 + 3, 422 | CURLOPT_PROXY = 10000 + 4, 423 | CURLOPT_USERPWD = 10000 + 5, 424 | CURLOPT_PROXYUSERPWD = 10000 + 6, 425 | CURLOPT_RANGE = 10000 + 7, 426 | CURLOPT_READDATA = 10000 + 9, 427 | CURLOPT_ERRORBUFFER = 10000 + 10, 428 | CURLOPT_WRITEFUNCTION = 20000 + 11, 429 | CURLOPT_READFUNCTION = 20000 + 12, 430 | CURLOPT_TIMEOUT = 0 + 13, 431 | CURLOPT_INFILESIZE = 0 + 14, 432 | CURLOPT_POSTFIELDS = 10000 + 15, 433 | CURLOPT_REFERER = 10000 + 16, 434 | CURLOPT_FTPPORT = 10000 + 17, 435 | CURLOPT_USERAGENT = 10000 + 18, 436 | CURLOPT_LOW_SPEED_LIMIT = 0 + 19, 437 | CURLOPT_LOW_SPEED_TIME = 0 + 20, 438 | CURLOPT_RESUME_FROM = 0 + 21, 439 | CURLOPT_COOKIE = 10000 + 22, 440 | CURLOPT_HTTPHEADER = 10000 + 23, 441 | CURLOPT_HTTPPOST = 10000 + 24, 442 | CURLOPT_SSLCERT = 10000 + 25, 443 | CURLOPT_KEYPASSWD = 10000 + 26, 444 | CURLOPT_CRLF = 0 + 27, 445 | CURLOPT_QUOTE = 10000 + 28, 446 | CURLOPT_HEADERDATA = 10000 + 29, 447 | CURLOPT_COOKIEFILE = 10000 + 31, 448 | CURLOPT_SSLVERSION = 0 + 32, 449 | CURLOPT_TIMECONDITION = 0 + 33, 450 | CURLOPT_TIMEVALUE = 0 + 34, 451 | CURLOPT_CUSTOMREQUEST = 10000 + 36, 452 | CURLOPT_STDERR = 10000 + 37, 453 | CURLOPT_POSTQUOTE = 10000 + 39, 454 | CURLOPT_OBSOLETE40 = 10000 + 40, 455 | CURLOPT_VERBOSE = 0 + 41, 456 | CURLOPT_HEADER = 0 + 42, 457 | CURLOPT_NOPROGRESS = 0 + 43, 458 | CURLOPT_NOBODY = 0 + 44, 459 | CURLOPT_FAILONERROR = 0 + 45, 460 | CURLOPT_UPLOAD = 0 + 46, 461 | CURLOPT_POST = 0 + 47, 462 | CURLOPT_DIRLISTONLY = 0 + 48, 463 | CURLOPT_APPEND = 0 + 50, 464 | CURLOPT_NETRC = 0 + 51, 465 | CURLOPT_FOLLOWLOCATION = 0 + 52, 466 | CURLOPT_TRANSFERTEXT = 0 + 53, 467 | CURLOPT_PUT = 0 + 54, 468 | CURLOPT_PROGRESSFUNCTION = 20000 + 56, 469 | CURLOPT_PROGRESSDATA = 10000 + 57, 470 | CURLOPT_AUTOREFERER = 0 + 58, 471 | CURLOPT_PROXYPORT = 0 + 59, 472 | CURLOPT_POSTFIELDSIZE = 0 + 60, 473 | CURLOPT_HTTPPROXYTUNNEL = 0 + 61, 474 | CURLOPT_INTERFACE = 10000 + 62, 475 | CURLOPT_KRBLEVEL = 10000 + 63, 476 | CURLOPT_SSL_VERIFYPEER = 0 + 64, 477 | CURLOPT_CAINFO = 10000 + 65, 478 | CURLOPT_MAXREDIRS = 0 + 68, 479 | CURLOPT_FILETIME = 0 + 69, 480 | CURLOPT_TELNETOPTIONS = 10000 + 70, 481 | CURLOPT_MAXCONNECTS = 0 + 71, 482 | CURLOPT_OBSOLETE72 = 0 + 72, 483 | CURLOPT_FRESH_CONNECT = 0 + 74, 484 | CURLOPT_FORBID_REUSE = 0 + 75, 485 | CURLOPT_RANDOM_FILE = 10000 + 76, 486 | CURLOPT_EGDSOCKET = 10000 + 77, 487 | CURLOPT_CONNECTTIMEOUT = 0 + 78, 488 | CURLOPT_HEADERFUNCTION = 20000 + 79, 489 | CURLOPT_HTTPGET = 0 + 80, 490 | CURLOPT_SSL_VERIFYHOST = 0 + 81, 491 | CURLOPT_COOKIEJAR = 10000 + 82, 492 | CURLOPT_SSL_CIPHER_LIST = 10000 + 83, 493 | CURLOPT_HTTP_VERSION = 0 + 84, 494 | CURLOPT_FTP_USE_EPSV = 0 + 85, 495 | CURLOPT_SSLCERTTYPE = 10000 + 86, 496 | CURLOPT_SSLKEY = 10000 + 87, 497 | CURLOPT_SSLKEYTYPE = 10000 + 88, 498 | CURLOPT_SSLENGINE = 10000 + 89, 499 | CURLOPT_SSLENGINE_DEFAULT = 0 + 90, 500 | CURLOPT_DNS_USE_GLOBAL_CACHE = 0 + 91, 501 | CURLOPT_DNS_CACHE_TIMEOUT = 0 + 92, 502 | CURLOPT_PREQUOTE = 10000 + 93, 503 | CURLOPT_DEBUGFUNCTION = 20000 + 94, 504 | CURLOPT_DEBUGDATA = 10000 + 95, 505 | CURLOPT_COOKIESESSION = 0 + 96, 506 | CURLOPT_CAPATH = 10000 + 97, 507 | CURLOPT_BUFFERSIZE = 0 + 98, 508 | CURLOPT_NOSIGNAL = 0 + 99, 509 | CURLOPT_SHARE = 10000 + 100, 510 | CURLOPT_PROXYTYPE = 0 + 101, 511 | CURLOPT_ACCEPT_ENCODING = 10000 + 102, 512 | CURLOPT_PRIVATE = 10000 + 103, 513 | CURLOPT_HTTP200ALIASES = 10000 + 104, 514 | CURLOPT_UNRESTRICTED_AUTH = 0 + 105, 515 | CURLOPT_FTP_USE_EPRT = 0 + 106, 516 | CURLOPT_HTTPAUTH = 0 + 107, 517 | CURLOPT_SSL_CTX_FUNCTION = 20000 + 108, 518 | CURLOPT_SSL_CTX_DATA = 10000 + 109, 519 | CURLOPT_FTP_CREATE_MISSING_DIRS = 0 + 110, 520 | CURLOPT_PROXYAUTH = 0 + 111, 521 | CURLOPT_FTP_RESPONSE_TIMEOUT = 0 + 112, 522 | CURLOPT_IPRESOLVE = 0 + 113, 523 | CURLOPT_MAXFILESIZE = 0 + 114, 524 | CURLOPT_INFILESIZE_LARGE = 30000 + 115, 525 | CURLOPT_RESUME_FROM_LARGE = 30000 + 116, 526 | CURLOPT_MAXFILESIZE_LARGE = 30000 + 117, 527 | CURLOPT_NETRC_FILE = 10000 + 118, 528 | CURLOPT_USE_SSL = 0 + 119, 529 | CURLOPT_POSTFIELDSIZE_LARGE = 30000 + 120, 530 | CURLOPT_TCP_NODELAY = 0 + 121, 531 | CURLOPT_FTPSSLAUTH = 0 + 129, 532 | CURLOPT_IOCTLFUNCTION = 20000 + 130, 533 | CURLOPT_IOCTLDATA = 10000 + 131, 534 | CURLOPT_FTP_ACCOUNT = 10000 + 134, 535 | CURLOPT_COOKIELIST = 10000 + 135, 536 | CURLOPT_IGNORE_CONTENT_LENGTH = 0 + 136, 537 | CURLOPT_FTP_SKIP_PASV_IP = 0 + 137, 538 | CURLOPT_FTP_FILEMETHOD = 0 + 138, 539 | CURLOPT_LOCALPORT = 0 + 139, 540 | CURLOPT_LOCALPORTRANGE = 0 + 140, 541 | CURLOPT_CONNECT_ONLY = 0 + 141, 542 | CURLOPT_CONV_FROM_NETWORK_FUNCTION = 20000 + 142, 543 | CURLOPT_CONV_TO_NETWORK_FUNCTION = 20000 + 143, 544 | CURLOPT_CONV_FROM_UTF8_FUNCTION = 20000 + 144, 545 | CURLOPT_MAX_SEND_SPEED_LARGE = 30000 + 145, 546 | CURLOPT_MAX_RECV_SPEED_LARGE = 30000 + 146, 547 | CURLOPT_FTP_ALTERNATIVE_TO_USER = 10000 + 147, 548 | CURLOPT_SOCKOPTFUNCTION = 20000 + 148, 549 | CURLOPT_SOCKOPTDATA = 10000 + 149, 550 | CURLOPT_SSL_SESSIONID_CACHE = 0 + 150, 551 | CURLOPT_SSH_AUTH_TYPES = 0 + 151, 552 | CURLOPT_SSH_PUBLIC_KEYFILE = 10000 + 152, 553 | CURLOPT_SSH_PRIVATE_KEYFILE = 10000 + 153, 554 | CURLOPT_FTP_SSL_CCC = 0 + 154, 555 | CURLOPT_TIMEOUT_MS = 0 + 155, 556 | CURLOPT_CONNECTTIMEOUT_MS = 0 + 156, 557 | CURLOPT_HTTP_TRANSFER_DECODING = 0 + 157, 558 | CURLOPT_HTTP_CONTENT_DECODING = 0 + 158, 559 | CURLOPT_NEW_FILE_PERMS = 0 + 159, 560 | CURLOPT_NEW_DIRECTORY_PERMS = 0 + 160, 561 | CURLOPT_POSTREDIR = 0 + 161, 562 | CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 = 10000 + 162, 563 | CURLOPT_OPENSOCKETFUNCTION = 20000 + 163, 564 | CURLOPT_OPENSOCKETDATA = 10000 + 164, 565 | CURLOPT_COPYPOSTFIELDS = 10000 + 165, 566 | CURLOPT_PROXY_TRANSFER_MODE = 0 + 166, 567 | CURLOPT_SEEKFUNCTION = 20000 + 167, 568 | CURLOPT_SEEKDATA = 10000 + 168, 569 | CURLOPT_CRLFILE = 10000 + 169, 570 | CURLOPT_ISSUERCERT = 10000 + 170, 571 | CURLOPT_ADDRESS_SCOPE = 0 + 171, 572 | CURLOPT_CERTINFO = 0 + 172, 573 | CURLOPT_USERNAME = 10000 + 173, 574 | CURLOPT_PASSWORD = 10000 + 174, 575 | CURLOPT_PROXYUSERNAME = 10000 + 175, 576 | CURLOPT_PROXYPASSWORD = 10000 + 176, 577 | CURLOPT_NOPROXY = 10000 + 177, 578 | CURLOPT_TFTP_BLKSIZE = 0 + 178, 579 | CURLOPT_SOCKS5_GSSAPI_SERVICE = 10000 + 179, 580 | CURLOPT_SOCKS5_GSSAPI_NEC = 0 + 180, 581 | CURLOPT_PROTOCOLS = 0 + 181, 582 | CURLOPT_REDIR_PROTOCOLS = 0 + 182, 583 | CURLOPT_SSH_KNOWNHOSTS = 10000 + 183, 584 | CURLOPT_SSH_KEYFUNCTION = 20000 + 184, 585 | CURLOPT_SSH_KEYDATA = 10000 + 185, 586 | CURLOPT_MAIL_FROM = 10000 + 186, 587 | CURLOPT_MAIL_RCPT = 10000 + 187, 588 | CURLOPT_FTP_USE_PRET = 0 + 188, 589 | CURLOPT_RTSP_REQUEST = 0 + 189, 590 | CURLOPT_RTSP_SESSION_ID = 10000 + 190, 591 | CURLOPT_RTSP_STREAM_URI = 10000 + 191, 592 | CURLOPT_RTSP_TRANSPORT = 10000 + 192, 593 | CURLOPT_RTSP_CLIENT_CSEQ = 0 + 193, 594 | CURLOPT_RTSP_SERVER_CSEQ = 0 + 194, 595 | CURLOPT_INTERLEAVEDATA = 10000 + 195, 596 | CURLOPT_INTERLEAVEFUNCTION = 20000 + 196, 597 | CURLOPT_WILDCARDMATCH = 0 + 197, 598 | CURLOPT_CHUNK_BGN_FUNCTION = 20000 + 198, 599 | CURLOPT_CHUNK_END_FUNCTION = 20000 + 199, 600 | CURLOPT_FNMATCH_FUNCTION = 20000 + 200, 601 | CURLOPT_CHUNK_DATA = 10000 + 201, 602 | CURLOPT_FNMATCH_DATA = 10000 + 202, 603 | CURLOPT_RESOLVE = 10000 + 203, 604 | CURLOPT_TLSAUTH_USERNAME = 10000 + 204, 605 | CURLOPT_TLSAUTH_PASSWORD = 10000 + 205, 606 | CURLOPT_TLSAUTH_TYPE = 10000 + 206, 607 | CURLOPT_TRANSFER_ENCODING = 0 + 207, 608 | CURLOPT_CLOSESOCKETFUNCTION = 20000 + 208, 609 | CURLOPT_CLOSESOCKETDATA = 10000 + 209, 610 | CURLOPT_GSSAPI_DELEGATION = 0 + 210, 611 | CURLOPT_DNS_SERVERS = 10000 + 211, 612 | CURLOPT_ACCEPTTIMEOUT_MS = 0 + 212, 613 | CURLOPT_TCP_KEEPALIVE = 0 + 213, 614 | CURLOPT_TCP_KEEPIDLE = 0 + 214, 615 | CURLOPT_TCP_KEEPINTVL = 0 + 215, 616 | CURLOPT_SSL_OPTIONS = 0 + 216, 617 | CURLOPT_MAIL_AUTH = 10000 + 217, 618 | CURLOPT_SASL_IR = 0 + 218, 619 | CURLOPT_XFERINFOFUNCTION = 20000 + 219, 620 | CURLOPT_XOAUTH2_BEARER = 10000 + 220, 621 | CURLOPT_DNS_INTERFACE = 10000 + 221, 622 | CURLOPT_DNS_LOCAL_IP4 = 10000 + 222, 623 | CURLOPT_DNS_LOCAL_IP6 = 10000 + 223, 624 | CURLOPT_LOGIN_OPTIONS = 10000 + 224, 625 | CURLOPT_SSL_ENABLE_NPN = 0 + 225, 626 | CURLOPT_SSL_ENABLE_ALPN = 0 + 226, 627 | CURLOPT_EXPECT_100_TIMEOUT_MS = 0 + 227, 628 | CURLOPT_PROXYHEADER = 10000 + 228, 629 | CURLOPT_HEADEROPT = 0 + 229, 630 | CURLOPT_LASTENTRY 631 | } CURLoption; 632 | enum { 633 | CURL_HTTP_VERSION_NONE, 634 | CURL_HTTP_VERSION_1_0, 635 | CURL_HTTP_VERSION_1_1, 636 | CURL_HTTP_VERSION_2_0, 637 | CURL_HTTP_VERSION_LAST 638 | }; 639 | enum { 640 | CURL_RTSPREQ_NONE, 641 | CURL_RTSPREQ_OPTIONS, 642 | CURL_RTSPREQ_DESCRIBE, 643 | CURL_RTSPREQ_ANNOUNCE, 644 | CURL_RTSPREQ_SETUP, 645 | CURL_RTSPREQ_PLAY, 646 | CURL_RTSPREQ_PAUSE, 647 | CURL_RTSPREQ_TEARDOWN, 648 | CURL_RTSPREQ_GET_PARAMETER, 649 | CURL_RTSPREQ_SET_PARAMETER, 650 | CURL_RTSPREQ_RECORD, 651 | CURL_RTSPREQ_RECEIVE, 652 | CURL_RTSPREQ_LAST 653 | }; 654 | enum CURL_NETRC_OPTION { 655 | CURL_NETRC_IGNORED, 656 | CURL_NETRC_OPTIONAL, 657 | CURL_NETRC_REQUIRED, 658 | CURL_NETRC_LAST 659 | }; 660 | enum { 661 | CURL_SSLVERSION_DEFAULT, 662 | CURL_SSLVERSION_TLSv1, 663 | CURL_SSLVERSION_SSLv2, 664 | CURL_SSLVERSION_SSLv3, 665 | CURL_SSLVERSION_TLSv1_0, 666 | CURL_SSLVERSION_TLSv1_1, 667 | CURL_SSLVERSION_TLSv1_2, 668 | CURL_SSLVERSION_LAST 669 | }; 670 | enum CURL_TLSAUTH { 671 | CURL_TLSAUTH_NONE, 672 | CURL_TLSAUTH_SRP, 673 | CURL_TLSAUTH_LAST 674 | }; 675 | typedef enum { 676 | CURL_TIMECOND_NONE, 677 | CURL_TIMECOND_IFMODSINCE, 678 | CURL_TIMECOND_IFUNMODSINCE, 679 | CURL_TIMECOND_LASTMOD, 680 | CURL_TIMECOND_LAST 681 | } curl_TimeCond; 682 | int (curl_strequal)(const char *s1, const char *s2); 683 | int (curl_strnequal)(const char *s1, const char *s2, size_t n); 684 | typedef enum { 685 | CURLFORM_NOTHING, 686 | CURLFORM_COPYNAME, 687 | CURLFORM_PTRNAME, 688 | CURLFORM_NAMELENGTH, 689 | CURLFORM_COPYCONTENTS, 690 | CURLFORM_PTRCONTENTS, 691 | CURLFORM_CONTENTSLENGTH, 692 | CURLFORM_FILECONTENT, 693 | CURLFORM_ARRAY, 694 | CURLFORM_OBSOLETE, 695 | CURLFORM_FILE, 696 | CURLFORM_BUFFER, 697 | CURLFORM_BUFFERPTR, 698 | CURLFORM_BUFFERLENGTH, 699 | CURLFORM_CONTENTTYPE, 700 | CURLFORM_CONTENTHEADER, 701 | CURLFORM_FILENAME, 702 | CURLFORM_END, 703 | CURLFORM_OBSOLETE2, 704 | CURLFORM_STREAM, 705 | CURLFORM_LASTENTRY 706 | } CURLformoption; 707 | struct curl_forms { 708 | CURLformoption option; 709 | const char *value; 710 | }; 711 | typedef enum { 712 | CURL_FORMADD_OK, 713 | CURL_FORMADD_MEMORY, 714 | CURL_FORMADD_OPTION_TWICE, 715 | CURL_FORMADD_NULL, 716 | CURL_FORMADD_UNKNOWN_OPTION, 717 | CURL_FORMADD_INCOMPLETE, 718 | CURL_FORMADD_ILLEGAL_ARRAY, 719 | CURL_FORMADD_DISABLED, 720 | CURL_FORMADD_LAST 721 | } CURLFORMcode; 722 | CURLFORMcode curl_formadd(struct curl_httppost **httppost, 723 | struct curl_httppost **last_post, 724 | ...); 725 | typedef size_t (*curl_formget_callback)(void *arg, const char *buf, 726 | size_t len); 727 | int curl_formget(struct curl_httppost *form, void *arg, 728 | curl_formget_callback append); 729 | void curl_formfree(struct curl_httppost *form); 730 | char *curl_getenv(const char *variable); 731 | char *curl_version(void); 732 | char *curl_easy_escape(CURL *handle, 733 | const char *string, 734 | int length); 735 | char *curl_escape(const char *string, 736 | int length); 737 | char *curl_easy_unescape(CURL *handle, 738 | const char *string, 739 | int length, 740 | int *outlength); 741 | char *curl_unescape(const char *string, 742 | int length); 743 | void curl_free(void *p); 744 | CURLcode curl_global_init(long flags); 745 | CURLcode curl_global_init_mem(long flags, 746 | curl_malloc_callback m, 747 | curl_free_callback f, 748 | curl_realloc_callback r, 749 | curl_strdup_callback s, 750 | curl_calloc_callback c); 751 | void curl_global_cleanup(void); 752 | struct curl_slist { 753 | char *data; 754 | struct curl_slist *next; 755 | }; 756 | struct curl_slist *curl_slist_append(struct curl_slist *, 757 | const char *); 758 | void curl_slist_free_all(struct curl_slist *); 759 | time_t curl_getdate(const char *p, const time_t *unused); 760 | struct curl_certinfo { 761 | int num_of_certs; 762 | struct curl_slist **certinfo; 763 | }; 764 | typedef enum { 765 | CURLSSLBACKEND_NONE = 0, 766 | CURLSSLBACKEND_OPENSSL = 1, 767 | CURLSSLBACKEND_GNUTLS = 2, 768 | CURLSSLBACKEND_NSS = 3, 769 | CURLSSLBACKEND_QSOSSL = 4, 770 | CURLSSLBACKEND_GSKIT = 5, 771 | CURLSSLBACKEND_POLARSSL = 6, 772 | CURLSSLBACKEND_CYASSL = 7, 773 | CURLSSLBACKEND_SCHANNEL = 8, 774 | CURLSSLBACKEND_DARWINSSL = 9, 775 | CURLSSLBACKEND_AXTLS = 10 776 | } curl_sslbackend; 777 | struct curl_tlssessioninfo { 778 | curl_sslbackend backend; 779 | void *internals; 780 | }; 781 | typedef enum { 782 | CURLINFO_NONE, 783 | CURLINFO_EFFECTIVE_URL =1048576 + 1, 784 | CURLINFO_RESPONSE_CODE =2097152 + 2, 785 | CURLINFO_TOTAL_TIME =3145728 + 3, 786 | CURLINFO_NAMELOOKUP_TIME =3145728 + 4, 787 | CURLINFO_CONNECT_TIME =3145728 + 5, 788 | CURLINFO_PRETRANSFER_TIME =3145728 + 6, 789 | CURLINFO_SIZE_UPLOAD =3145728 + 7, 790 | CURLINFO_SIZE_DOWNLOAD =3145728 + 8, 791 | CURLINFO_SPEED_DOWNLOAD =3145728 + 9, 792 | CURLINFO_SPEED_UPLOAD =3145728 + 10, 793 | CURLINFO_HEADER_SIZE =2097152 + 11, 794 | CURLINFO_REQUEST_SIZE =2097152 + 12, 795 | CURLINFO_SSL_VERIFYRESULT =2097152 + 13, 796 | CURLINFO_FILETIME =2097152 + 14, 797 | CURLINFO_CONTENT_LENGTH_DOWNLOAD =3145728 + 15, 798 | CURLINFO_CONTENT_LENGTH_UPLOAD =3145728 + 16, 799 | CURLINFO_STARTTRANSFER_TIME =3145728 + 17, 800 | CURLINFO_CONTENT_TYPE =1048576 + 18, 801 | CURLINFO_REDIRECT_TIME =3145728 + 19, 802 | CURLINFO_REDIRECT_COUNT =2097152 + 20, 803 | CURLINFO_PRIVATE =1048576 + 21, 804 | CURLINFO_HTTP_CONNECTCODE =2097152 + 22, 805 | CURLINFO_HTTPAUTH_AVAIL =2097152 + 23, 806 | CURLINFO_PROXYAUTH_AVAIL =2097152 + 24, 807 | CURLINFO_OS_ERRNO =2097152 + 25, 808 | CURLINFO_NUM_CONNECTS =2097152 + 26, 809 | CURLINFO_SSL_ENGINES =4194304 + 27, 810 | CURLINFO_COOKIELIST =4194304 + 28, 811 | CURLINFO_LASTSOCKET =2097152 + 29, 812 | CURLINFO_FTP_ENTRY_PATH =1048576 + 30, 813 | CURLINFO_REDIRECT_URL =1048576 + 31, 814 | CURLINFO_PRIMARY_IP =1048576 + 32, 815 | CURLINFO_APPCONNECT_TIME =3145728 + 33, 816 | CURLINFO_CERTINFO =4194304 + 34, 817 | CURLINFO_CONDITION_UNMET =2097152 + 35, 818 | CURLINFO_RTSP_SESSION_ID =1048576 + 36, 819 | CURLINFO_RTSP_CLIENT_CSEQ =2097152 + 37, 820 | CURLINFO_RTSP_SERVER_CSEQ =2097152 + 38, 821 | CURLINFO_RTSP_CSEQ_RECV =2097152 + 39, 822 | CURLINFO_PRIMARY_PORT =2097152 + 40, 823 | CURLINFO_LOCAL_IP =1048576 + 41, 824 | CURLINFO_LOCAL_PORT =2097152 + 42, 825 | CURLINFO_TLS_SESSION =4194304 + 43, 826 | CURLINFO_LASTONE = 43 827 | } CURLINFO; 828 | typedef enum { 829 | CURLCLOSEPOLICY_NONE, 830 | CURLCLOSEPOLICY_OLDEST, 831 | CURLCLOSEPOLICY_LEAST_RECENTLY_USED, 832 | CURLCLOSEPOLICY_LEAST_TRAFFIC, 833 | CURLCLOSEPOLICY_SLOWEST, 834 | CURLCLOSEPOLICY_CALLBACK, 835 | CURLCLOSEPOLICY_LAST 836 | } curl_closepolicy; 837 | typedef enum { 838 | CURL_LOCK_DATA_NONE = 0, 839 | CURL_LOCK_DATA_SHARE, 840 | CURL_LOCK_DATA_COOKIE, 841 | CURL_LOCK_DATA_DNS, 842 | CURL_LOCK_DATA_SSL_SESSION, 843 | CURL_LOCK_DATA_CONNECT, 844 | CURL_LOCK_DATA_LAST 845 | } curl_lock_data; 846 | typedef enum { 847 | CURL_LOCK_ACCESS_NONE = 0, 848 | CURL_LOCK_ACCESS_SHARED = 1, 849 | CURL_LOCK_ACCESS_SINGLE = 2, 850 | CURL_LOCK_ACCESS_LAST 851 | } curl_lock_access; 852 | typedef void (*curl_lock_function)(CURL *handle, 853 | curl_lock_data data, 854 | curl_lock_access locktype, 855 | void *userptr); 856 | typedef void (*curl_unlock_function)(CURL *handle, 857 | curl_lock_data data, 858 | void *userptr); 859 | typedef void CURLSH; 860 | typedef enum { 861 | CURLSHE_OK, 862 | CURLSHE_BAD_OPTION, 863 | CURLSHE_IN_USE, 864 | CURLSHE_INVALID, 865 | CURLSHE_NOMEM, 866 | CURLSHE_NOT_BUILT_IN, 867 | CURLSHE_LAST 868 | } CURLSHcode; 869 | typedef enum { 870 | CURLSHOPT_NONE, 871 | CURLSHOPT_SHARE, 872 | CURLSHOPT_UNSHARE, 873 | CURLSHOPT_LOCKFUNC, 874 | CURLSHOPT_UNLOCKFUNC, 875 | CURLSHOPT_USERDATA, 876 | CURLSHOPT_LAST 877 | } CURLSHoption; 878 | CURLSH *curl_share_init(void); 879 | CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...); 880 | CURLSHcode curl_share_cleanup(CURLSH *); 881 | typedef enum { 882 | CURLVERSION_FIRST, 883 | CURLVERSION_SECOND, 884 | CURLVERSION_THIRD, 885 | CURLVERSION_FOURTH, 886 | CURLVERSION_LAST 887 | } CURLversion; 888 | typedef struct { 889 | CURLversion age; 890 | const char *version; 891 | unsigned int version_num; 892 | const char *host; 893 | int features; 894 | const char *ssl_version; 895 | long ssl_version_num; 896 | const char *libz_version; 897 | const char * const *protocols; 898 | const char *ares; 899 | int ares_num; 900 | const char *libidn; 901 | int iconv_ver_num; 902 | const char *libssh_version; 903 | } curl_version_info_data; 904 | curl_version_info_data *curl_version_info(CURLversion); 905 | const char *curl_easy_strerror(CURLcode); 906 | const char *curl_share_strerror(CURLSHcode); 907 | CURLcode curl_easy_pause(CURL *handle, int bitmask); 908 | CURL *curl_easy_init(void); 909 | CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); 910 | CURLcode curl_easy_perform(CURL *curl); 911 | void curl_easy_cleanup(CURL *curl); 912 | CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); 913 | CURL* curl_easy_duphandle(CURL *curl); 914 | void curl_easy_reset(CURL *curl); 915 | CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, 916 | size_t *n); 917 | CURLcode curl_easy_send(CURL *curl, const void *buffer, 918 | size_t buflen, size_t *n); 919 | typedef void CURLM; 920 | typedef enum { 921 | CURLM_CALL_MULTI_PERFORM = -1, 922 | CURLM_OK, 923 | CURLM_BAD_HANDLE, 924 | CURLM_BAD_EASY_HANDLE, 925 | CURLM_OUT_OF_MEMORY, 926 | CURLM_INTERNAL_ERROR, 927 | CURLM_BAD_SOCKET, 928 | CURLM_UNKNOWN_OPTION, 929 | CURLM_ADDED_ALREADY, 930 | CURLM_LAST 931 | } CURLMcode; 932 | typedef enum { 933 | CURLMSG_NONE, 934 | CURLMSG_DONE, 935 | CURLMSG_LAST 936 | } CURLMSG; 937 | struct CURLMsg { 938 | CURLMSG msg; 939 | CURL *easy_handle; 940 | union { 941 | void *whatever; 942 | CURLcode result; 943 | } data; 944 | }; 945 | typedef struct CURLMsg CURLMsg; 946 | struct curl_waitfd { 947 | curl_socket_t fd; 948 | short events; 949 | short revents; 950 | }; 951 | typedef struct fd_set { 952 | unsigned int fd_count; /* how many are SET? */ 953 | curl_socket_t fd_array[64]; //FD_SETSIZE, 64 on my machine, TOFIX 954 | } fd_set; 955 | CURLM *curl_multi_init(void); 956 | CURLMcode curl_multi_add_handle(CURLM *multi_handle, 957 | CURL *curl_handle); 958 | CURLMcode curl_multi_remove_handle(CURLM *multi_handle, 959 | CURL *curl_handle); 960 | CURLMcode curl_multi_fdset(CURLM *multi_handle, 961 | fd_set *read_fd_set, 962 | fd_set *write_fd_set, 963 | fd_set *exc_fd_set, 964 | int *max_fd); 965 | CURLMcode curl_multi_wait(CURLM *multi_handle, 966 | struct curl_waitfd extra_fds[], 967 | unsigned int extra_nfds, 968 | int timeout_ms, 969 | int *ret); 970 | CURLMcode curl_multi_perform(CURLM *multi_handle, 971 | int *running_handles); 972 | CURLMcode curl_multi_cleanup(CURLM *multi_handle); 973 | CURLMsg *curl_multi_info_read(CURLM *multi_handle, 974 | int *msgs_in_queue); 975 | const char *curl_multi_strerror(CURLMcode); 976 | typedef int (*curl_socket_callback)(CURL *easy, 977 | curl_socket_t s, 978 | int what, 979 | void *userp, 980 | void *socketp); 981 | typedef int (*curl_multi_timer_callback)(CURLM *multi, 982 | long timeout_ms, 983 | void *userp); 984 | CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, 985 | int *running_handles); 986 | CURLMcode curl_multi_socket_action(CURLM *multi_handle, 987 | curl_socket_t s, 988 | int ev_bitmask, 989 | int *running_handles); 990 | CURLMcode curl_multi_socket_all(CURLM *multi_handle, 991 | int *running_handles); 992 | CURLMcode curl_multi_timeout(CURLM *multi_handle, 993 | long *milliseconds); 994 | typedef enum { 995 | CURLMOPT_SOCKETFUNCTION = 20000 + 1, 996 | CURLMOPT_SOCKETDATA = 10000 + 2, 997 | CURLMOPT_PIPELINING = 0 + 3, 998 | CURLMOPT_TIMERFUNCTION = 20000 + 4, 999 | CURLMOPT_TIMERDATA = 10000 + 5, 1000 | CURLMOPT_MAXCONNECTS = 0 + 6, 1001 | CURLMOPT_MAX_HOST_CONNECTIONS = 0 + 7, 1002 | CURLMOPT_MAX_PIPELINE_LENGTH = 0 + 8, 1003 | CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE = 30000 + 9, 1004 | CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE = 30000 + 10, 1005 | CURLMOPT_PIPELINING_SITE_BL = 10000 + 11, 1006 | CURLMOPT_PIPELINING_SERVER_BL = 10000 + 12, 1007 | CURLMOPT_MAX_TOTAL_CONNECTIONS = 0 + 13, 1008 | CURLMOPT_LASTENTRY 1009 | } CURLMoption; 1010 | CURLMcode curl_multi_setopt(CURLM *multi_handle, 1011 | CURLMoption option, ...); 1012 | CURLMcode curl_multi_assign(CURLM *multi_handle, 1013 | curl_socket_t sockfd, void *sockp); 1014 | ]]) 1015 | 1016 | return curl -------------------------------------------------------------------------------- /automation/include/utf8.lua: -------------------------------------------------------------------------------- 1 | -- $Id: utf8.lua 179 2009-04-03 18:10:03Z pasta $ 2 | -- 3 | -- Provides UTF-8 aware string functions implemented in pure lua: 4 | -- * utf8len(s) 5 | -- * utf8sub(s, i, j) 6 | -- * utf8reverse(s) 7 | -- * utf8char(unicode) 8 | -- * utf8unicode(s, i, j) 9 | -- * utf8gensub(s, sub_len) 10 | -- * utf8find(str, regex, init, plain) 11 | -- * utf8match(str, regex, init) 12 | -- * utf8gmatch(str, regex, all) 13 | -- * utf8gsub(str, regex, repl, limit) 14 | -- 15 | -- If utf8data.lua (containing the lower<->upper case mappings) is loaded, these 16 | -- additional functions are available: 17 | -- * utf8upper(s) 18 | -- * utf8lower(s) 19 | -- 20 | -- All functions behave as their non UTF-8 aware counterparts with the exception 21 | -- that UTF-8 characters are used instead of bytes for all units. 22 | 23 | --[[ 24 | Copyright (c) 2006-2007, Kyle Smith 25 | All rights reserved. 26 | 27 | Contributors: 28 | Alimov Stepan 29 | 30 | Redistribution and use in source and binary forms, with or without 31 | modification, are permitted provided that the following conditions are met: 32 | 33 | * Redistributions of source code must retain the above copyright notice, 34 | this list of conditions and the following disclaimer. 35 | * Redistributions in binary form must reproduce the above copyright 36 | notice, this list of conditions and the following disclaimer in the 37 | documentation and/or other materials provided with the distribution. 38 | * Neither the name of the author nor the names of its contributors may be 39 | used to endorse or promote products derived from this software without 40 | specific prior written permission. 41 | 42 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 43 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 44 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 45 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 46 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 47 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 48 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 49 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 51 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 52 | --]] 53 | 54 | -- ABNF from RFC 3629 55 | -- 56 | -- UTF8-octets = *( UTF8-char ) 57 | -- UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 58 | -- UTF8-1 = %x00-7F 59 | -- UTF8-2 = %xC2-DF UTF8-tail 60 | -- UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / 61 | -- %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) 62 | -- UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / 63 | -- %xF4 %x80-8F 2( UTF8-tail ) 64 | -- UTF8-tail = %x80-BF 65 | -- 66 | 67 | local byte = string.byte 68 | local char = string.char 69 | local dump = string.dump 70 | local find = string.find 71 | local format = string.format 72 | local len = string.len 73 | local lower = string.lower 74 | local rep = string.rep 75 | local sub = string.sub 76 | local upper = string.upper 77 | 78 | -- returns the number of bytes used by the UTF-8 character at byte i in s 79 | -- also doubles as a UTF-8 character validator 80 | local function utf8charbytes (s, i) 81 | -- argument defaults 82 | i = i or 1 83 | 84 | -- argument checking 85 | if type(s) ~= "string" then 86 | error("bad argument #1 to 'utf8charbytes' (string expected, got ".. type(s).. ")") 87 | end 88 | if type(i) ~= "number" then 89 | error("bad argument #2 to 'utf8charbytes' (number expected, got ".. type(i).. ")") 90 | end 91 | 92 | local c = byte(s, i) 93 | 94 | -- determine bytes needed for character, based on RFC 3629 95 | -- validate byte 1 96 | if c > 0 and c <= 127 then 97 | -- UTF8-1 98 | return 1 99 | 100 | elseif c >= 194 and c <= 223 then 101 | -- UTF8-2 102 | local c2 = byte(s, i + 1) 103 | 104 | if not c2 then 105 | error("UTF-8 string terminated early") 106 | end 107 | 108 | -- validate byte 2 109 | if c2 < 128 or c2 > 191 then 110 | error("Invalid UTF-8 character") 111 | end 112 | 113 | return 2 114 | 115 | elseif c >= 224 and c <= 239 then 116 | -- UTF8-3 117 | local c2 = byte(s, i + 1) 118 | local c3 = byte(s, i + 2) 119 | 120 | if not c2 or not c3 then 121 | error("UTF-8 string terminated early") 122 | end 123 | 124 | -- validate byte 2 125 | if c == 224 and (c2 < 160 or c2 > 191) then 126 | error("Invalid UTF-8 character") 127 | elseif c == 237 and (c2 < 128 or c2 > 159) then 128 | error("Invalid UTF-8 character") 129 | elseif c2 < 128 or c2 > 191 then 130 | error("Invalid UTF-8 character") 131 | end 132 | 133 | -- validate byte 3 134 | if c3 < 128 or c3 > 191 then 135 | error("Invalid UTF-8 character") 136 | end 137 | 138 | return 3 139 | 140 | elseif c >= 240 and c <= 244 then 141 | -- UTF8-4 142 | local c2 = byte(s, i + 1) 143 | local c3 = byte(s, i + 2) 144 | local c4 = byte(s, i + 3) 145 | 146 | if not c2 or not c3 or not c4 then 147 | error("UTF-8 string terminated early") 148 | end 149 | 150 | -- validate byte 2 151 | if c == 240 and (c2 < 144 or c2 > 191) then 152 | error("Invalid UTF-8 character") 153 | elseif c == 244 and (c2 < 128 or c2 > 143) then 154 | error("Invalid UTF-8 character") 155 | elseif c2 < 128 or c2 > 191 then 156 | error("Invalid UTF-8 character") 157 | end 158 | 159 | -- validate byte 3 160 | if c3 < 128 or c3 > 191 then 161 | error("Invalid UTF-8 character") 162 | end 163 | 164 | -- validate byte 4 165 | if c4 < 128 or c4 > 191 then 166 | error("Invalid UTF-8 character") 167 | end 168 | 169 | return 4 170 | 171 | else 172 | error("Invalid UTF-8 character") 173 | end 174 | end 175 | 176 | -- returns the number of characters in a UTF-8 string 177 | local function utf8len (s) 178 | -- argument checking 179 | if type(s) ~= "string" then 180 | for k,v in pairs(s) do print('"',tostring(k),'"',tostring(v),'"') end 181 | error("bad argument #1 to 'utf8len' (string expected, got ".. type(s).. ")") 182 | end 183 | 184 | local pos = 1 185 | local bytes = len(s) 186 | local length = 0 187 | 188 | while pos <= bytes do 189 | length = length + 1 190 | pos = pos + utf8charbytes(s, pos) 191 | end 192 | 193 | return length 194 | end 195 | 196 | -- functions identically to string.sub except that i and j are UTF-8 characters 197 | -- instead of bytes 198 | local function utf8sub (s, i, j) 199 | -- argument defaults 200 | j = j or -1 201 | 202 | local pos = 1 203 | local bytes = len(s) 204 | local length = 0 205 | 206 | -- only set l if i or j is negative 207 | local l = (i >= 0 and j >= 0) or utf8len(s) 208 | local startChar = (i >= 0) and i or l + i + 1 209 | local endChar = (j >= 0) and j or l + j + 1 210 | 211 | -- can't have start before end! 212 | if startChar > endChar then 213 | return "" 214 | end 215 | 216 | -- byte offsets to pass to string.sub 217 | local startByte,endByte = 1,bytes 218 | 219 | while pos <= bytes do 220 | length = length + 1 221 | 222 | if length == startChar then 223 | startByte = pos 224 | end 225 | 226 | pos = pos + utf8charbytes(s, pos) 227 | 228 | if length == endChar then 229 | endByte = pos - 1 230 | break 231 | end 232 | end 233 | 234 | if startChar > length then startByte = bytes+1 end 235 | if endChar < 1 then endByte = 0 end 236 | 237 | return sub(s, startByte, endByte) 238 | end 239 | 240 | --[[ 241 | -- replace UTF-8 characters based on a mapping table 242 | local function utf8replace (s, mapping) 243 | -- argument checking 244 | if type(s) ~= "string" then 245 | error("bad argument #1 to 'utf8replace' (string expected, got ".. type(s).. ")") 246 | end 247 | if type(mapping) ~= "table" then 248 | error("bad argument #2 to 'utf8replace' (table expected, got ".. type(mapping).. ")") 249 | end 250 | 251 | local pos = 1 252 | local bytes = len(s) 253 | local charbytes 254 | local newstr = "" 255 | 256 | while pos <= bytes do 257 | charbytes = utf8charbytes(s, pos) 258 | local c = sub(s, pos, pos + charbytes - 1) 259 | 260 | newstr = newstr .. (mapping[c] or c) 261 | 262 | pos = pos + charbytes 263 | end 264 | 265 | return newstr 266 | end 267 | 268 | 269 | -- identical to string.upper except it knows about unicode simple case conversions 270 | local function utf8upper (s) 271 | return utf8replace(s, utf8_lc_uc) 272 | end 273 | 274 | -- identical to string.lower except it knows about unicode simple case conversions 275 | local function utf8lower (s) 276 | return utf8replace(s, utf8_uc_lc) 277 | end 278 | ]] 279 | 280 | -- identical to string.reverse except that it supports UTF-8 281 | local function utf8reverse (s) 282 | -- argument checking 283 | if type(s) ~= "string" then 284 | error("bad argument #1 to 'utf8reverse' (string expected, got ".. type(s).. ")") 285 | end 286 | 287 | local bytes = len(s) 288 | local pos = bytes 289 | local charbytes 290 | local newstr = "" 291 | 292 | while pos > 0 do 293 | local c = byte(s, pos) 294 | while c >= 128 and c <= 191 do 295 | pos = pos - 1 296 | c = byte(s, pos) 297 | end 298 | 299 | charbytes = utf8charbytes(s, pos) 300 | 301 | newstr = newstr .. sub(s, pos, pos + charbytes - 1) 302 | 303 | pos = pos - 1 304 | end 305 | 306 | return newstr 307 | end 308 | 309 | -- http://en.wikipedia.org/wiki/Utf8 310 | -- http://developer.coronalabs.com/code/utf-8-conversion-utility 311 | local function utf8char(unicode) 312 | if unicode <= 0x7F then return char(unicode) end 313 | 314 | if (unicode <= 0x7FF) then 315 | local Byte0 = 0xC0 + math.floor(unicode / 0x40); 316 | local Byte1 = 0x80 + (unicode % 0x40); 317 | return char(Byte0, Byte1); 318 | end; 319 | 320 | if (unicode <= 0xFFFF) then 321 | local Byte0 = 0xE0 + math.floor(unicode / 0x1000); 322 | local Byte1 = 0x80 + (math.floor(unicode / 0x40) % 0x40); 323 | local Byte2 = 0x80 + (unicode % 0x40); 324 | return char(Byte0, Byte1, Byte2); 325 | end; 326 | 327 | if (unicode <= 0x10FFFF) then 328 | local code = unicode 329 | local Byte3= 0x80 + (code % 0x40); 330 | code = math.floor(code / 0x40) 331 | local Byte2= 0x80 + (code % 0x40); 332 | code = math.floor(code / 0x40) 333 | local Byte1= 0x80 + (code % 0x40); 334 | code = math.floor(code / 0x40) 335 | local Byte0= 0xF0 + code; 336 | 337 | return char(Byte0, Byte1, Byte2, Byte3); 338 | end; 339 | 340 | error 'Unicode cannot be greater than U+10FFFF!' 341 | end 342 | 343 | local shift_6 = 2^6 344 | local shift_12 = 2^12 345 | local shift_18 = 2^18 346 | 347 | local utf8unicode 348 | utf8unicode = function(str, i, j, byte_pos) 349 | i = i or 1 350 | j = j or i 351 | 352 | if i > j then return end 353 | 354 | local ch,bytes 355 | 356 | if byte_pos then 357 | bytes = utf8charbytes(str,byte_pos) 358 | ch = sub(str,byte_pos,byte_pos-1+bytes) 359 | else 360 | ch,byte_pos = utf8sub(str,i,i), 0 361 | bytes = #ch 362 | end 363 | 364 | local unicode 365 | 366 | if bytes == 1 then unicode = byte(ch) end 367 | if bytes == 2 then 368 | local byte0,byte1 = byte(ch,1,2) 369 | local code0,code1 = byte0-0xC0,byte1-0x80 370 | unicode = code0*shift_6 + code1 371 | end 372 | if bytes == 3 then 373 | local byte0,byte1,byte2 = byte(ch,1,3) 374 | local code0,code1,code2 = byte0-0xE0,byte1-0x80,byte2-0x80 375 | unicode = code0*shift_12 + code1*shift_6 + code2 376 | end 377 | if bytes == 4 then 378 | local byte0,byte1,byte2,byte3 = byte(ch,1,4) 379 | local code0,code1,code2,code3 = byte0-0xF0,byte1-0x80,byte2-0x80,byte3-0x80 380 | unicode = code0*shift_18 + code1*shift_12 + code2*shift_6 + code3 381 | end 382 | 383 | return unicode,utf8unicode(str, i+1, j, byte_pos+bytes) 384 | end 385 | 386 | -- Returns an iterator which returns the next substring and its byte interval 387 | local function utf8gensub(str, sub_len) 388 | sub_len = sub_len or 1 389 | local byte_pos = 1 390 | local length = #str 391 | return function(skip) 392 | if skip then byte_pos = byte_pos + skip end 393 | local char_count = 0 394 | local start = byte_pos 395 | repeat 396 | if byte_pos > length then return end 397 | char_count = char_count + 1 398 | local bytes = utf8charbytes(str,byte_pos) 399 | byte_pos = byte_pos+bytes 400 | 401 | until char_count == sub_len 402 | 403 | local last = byte_pos-1 404 | local slice = sub(str,start,last) 405 | return slice, start, last 406 | end 407 | end 408 | 409 | local function binsearch(sortedTable, item, comp) 410 | local head, tail = 1, #sortedTable 411 | local mid = math.floor((head + tail)/2) 412 | if not comp then 413 | while (tail - head) > 1 do 414 | if sortedTable[tonumber(mid)] > item then 415 | tail = mid 416 | else 417 | head = mid 418 | end 419 | mid = math.floor((head + tail)/2) 420 | end 421 | end 422 | if sortedTable[tonumber(head)] == item then 423 | return true, tonumber(head) 424 | elseif sortedTable[tonumber(tail)] == item then 425 | return true, tonumber(tail) 426 | else 427 | return false 428 | end 429 | end 430 | local function classMatchGenerator(class, plain) 431 | local codes = {} 432 | local ranges = {} 433 | local ignore = false 434 | local range = false 435 | local firstletter = true 436 | local unmatch = false 437 | 438 | local it = utf8gensub(class) 439 | 440 | local skip 441 | for c, _, be in it do 442 | skip = be 443 | if not ignore and not plain then 444 | if c == "%" then 445 | ignore = true 446 | elseif c == "-" then 447 | table.insert(codes, utf8unicode(c)) 448 | range = true 449 | elseif c == "^" then 450 | if not firstletter then 451 | error('!!!') 452 | else 453 | unmatch = true 454 | end 455 | elseif c == ']' then 456 | break 457 | else 458 | if not range then 459 | table.insert(codes, utf8unicode(c)) 460 | else 461 | table.remove(codes) -- removing '-' 462 | table.insert(ranges, {table.remove(codes), utf8unicode(c)}) 463 | range = false 464 | end 465 | end 466 | elseif ignore and not plain then 467 | if c == 'a' then -- %a: represents all letters. (ONLY ASCII) 468 | table.insert(ranges, {65, 90}) -- A - Z 469 | table.insert(ranges, {97, 122}) -- a - z 470 | elseif c == 'c' then -- %c: represents all control characters. 471 | table.insert(ranges, {0, 31}) 472 | table.insert(codes, 127) 473 | elseif c == 'd' then -- %d: represents all digits. 474 | table.insert(ranges, {48, 57}) -- 0 - 9 475 | elseif c == 'g' then -- %g: represents all printable characters except space. 476 | table.insert(ranges, {1, 8}) 477 | table.insert(ranges, {14, 31}) 478 | table.insert(ranges, {33, 132}) 479 | table.insert(ranges, {134, 159}) 480 | table.insert(ranges, {161, 5759}) 481 | table.insert(ranges, {5761, 8191}) 482 | table.insert(ranges, {8203, 8231}) 483 | table.insert(ranges, {8234, 8238}) 484 | table.insert(ranges, {8240, 8286}) 485 | table.insert(ranges, {8288, 12287}) 486 | elseif c == 'l' then -- %l: represents all lowercase letters. (ONLY ASCII) 487 | table.insert(ranges, {97, 122}) -- a - z 488 | elseif c == 'p' then -- %p: represents all punctuation characters. (ONLY ASCII) 489 | table.insert(ranges, {33, 47}) 490 | table.insert(ranges, {58, 64}) 491 | table.insert(ranges, {91, 96}) 492 | table.insert(ranges, {123, 126}) 493 | elseif c == 's' then -- %s: represents all space characters. 494 | table.insert(ranges, {9, 13}) 495 | table.insert(codes, 32) 496 | table.insert(codes, 133) 497 | table.insert(codes, 160) 498 | table.insert(codes, 5760) 499 | table.insert(ranges, {8192, 8202}) 500 | table.insert(codes, 8232) 501 | table.insert(codes, 8233) 502 | table.insert(codes, 8239) 503 | table.insert(codes, 8287) 504 | table.insert(codes, 12288) 505 | elseif c == 'u' then -- %u: represents all uppercase letters. (ONLY ASCII) 506 | table.insert(ranges, {65, 90}) -- A - Z 507 | elseif c == 'w' then -- %w: represents all alphanumeric characters. (ONLY ASCII) 508 | table.insert(ranges, {48, 57}) -- 0 - 9 509 | table.insert(ranges, {65, 90}) -- A - Z 510 | table.insert(ranges, {97, 122}) -- a - z 511 | elseif c == 'x' then -- %x: represents all hexadecimal digits. 512 | table.insert(ranges, {48, 57}) -- 0 - 9 513 | table.insert(ranges, {65, 70}) -- A - F 514 | table.insert(ranges, {97, 102}) -- a - f 515 | else 516 | if not range then 517 | table.insert(codes, utf8unicode(c)) 518 | else 519 | table.remove(codes) -- removing '-' 520 | table.insert(ranges, {table.remove(codes), utf8unicode(c)}) 521 | range = false 522 | end 523 | end 524 | ignore = false 525 | else 526 | if not range then 527 | table.insert(codes, utf8unicode(c)) 528 | else 529 | table.remove(codes) -- removing '-' 530 | table.insert(ranges, {table.remove(codes), utf8unicode(c)}) 531 | range = false 532 | end 533 | ignore = false 534 | end 535 | 536 | firstletter = false 537 | end 538 | 539 | table.sort(codes) 540 | 541 | local function inRanges(charCode) 542 | for _,r in ipairs(ranges) do 543 | if r[1] <= charCode and charCode <= r[2] then 544 | return true 545 | end 546 | end 547 | return false 548 | end 549 | if not unmatch then 550 | return function(charCode) 551 | return binsearch(codes, charCode) or inRanges(charCode) 552 | end, skip 553 | else 554 | return function(charCode) 555 | return charCode ~= -1 and not (binsearch(codes, charCode) or inRanges(charCode)) 556 | end, skip 557 | end 558 | end 559 | 560 | --[[ 561 | -- utf8sub with extra argument, and extra result value 562 | local function utf8subWithBytes (s, i, j, sb) 563 | -- argument defaults 564 | j = j or -1 565 | 566 | local pos = sb or 1 567 | local bytes = len(s) 568 | local length = 0 569 | 570 | -- only set l if i or j is negative 571 | local l = (i >= 0 and j >= 0) or utf8len(s) 572 | local startChar = (i >= 0) and i or l + i + 1 573 | local endChar = (j >= 0) and j or l + j + 1 574 | 575 | -- can't have start before end! 576 | if startChar > endChar then 577 | return "" 578 | end 579 | 580 | -- byte offsets to pass to string.sub 581 | local startByte,endByte = 1,bytes 582 | 583 | while pos <= bytes do 584 | length = length + 1 585 | 586 | if length == startChar then 587 | startByte = pos 588 | end 589 | 590 | pos = pos + utf8charbytes(s, pos) 591 | 592 | if length == endChar then 593 | endByte = pos - 1 594 | break 595 | end 596 | end 597 | 598 | if startChar > length then startByte = bytes+1 end 599 | if endChar < 1 then endByte = 0 end 600 | 601 | return sub(s, startByte, endByte), endByte + 1 602 | end 603 | ]] 604 | 605 | local cache = setmetatable({},{ 606 | __mode = 'kv' 607 | }) 608 | local cachePlain = setmetatable({},{ 609 | __mode = 'kv' 610 | }) 611 | local function matcherGenerator(regex, plain) 612 | local matcher = { 613 | functions = {}, 614 | captures = {} 615 | } 616 | if not plain then 617 | cache[regex] = matcher 618 | else 619 | cachePlain[regex] = matcher 620 | end 621 | local function simple(func) 622 | return function(cC) 623 | if func(cC) then 624 | matcher:nextFunc() 625 | matcher:nextStr() 626 | else 627 | matcher:reset() 628 | end 629 | end 630 | end 631 | local function star(func) 632 | return function(cC) 633 | if func(cC) then 634 | matcher:fullResetOnNextFunc() 635 | matcher:nextStr() 636 | else 637 | matcher:nextFunc() 638 | end 639 | end 640 | end 641 | local function minus(func) 642 | return function(cC) 643 | if func(cC) then 644 | matcher:fullResetOnNextStr() 645 | end 646 | matcher:nextFunc() 647 | end 648 | end 649 | local function question(func) 650 | return function(cC) 651 | if func(cC) then 652 | matcher:fullResetOnNextFunc() 653 | matcher:nextStr() 654 | end 655 | matcher:nextFunc() 656 | end 657 | end 658 | 659 | local function capture(id) 660 | return function(_) 661 | local l = matcher.captures[id][2] - matcher.captures[id][1] 662 | local captured = utf8sub(matcher.string, matcher.captures[id][1], matcher.captures[id][2]) 663 | local check = utf8sub(matcher.string, matcher.str, matcher.str + l) 664 | if captured == check then 665 | for _ = 0, l do 666 | matcher:nextStr() 667 | end 668 | matcher:nextFunc() 669 | else 670 | matcher:reset() 671 | end 672 | end 673 | end 674 | local function captureStart(id) 675 | return function(_) 676 | matcher.captures[id][1] = matcher.str 677 | matcher:nextFunc() 678 | end 679 | end 680 | local function captureStop(id) 681 | return function(_) 682 | matcher.captures[id][2] = matcher.str - 1 683 | matcher:nextFunc() 684 | end 685 | end 686 | 687 | local function balancer(str) 688 | local sum = 0 689 | local bc, ec = utf8sub(str, 1, 1), utf8sub(str, 2, 2) 690 | local skip = len(bc) + len(ec) 691 | bc, ec = utf8unicode(bc), utf8unicode(ec) 692 | return function(cC) 693 | if cC == ec and sum > 0 then 694 | sum = sum - 1 695 | if sum == 0 then 696 | matcher:nextFunc() 697 | end 698 | matcher:nextStr() 699 | elseif cC == bc then 700 | sum = sum + 1 701 | matcher:nextStr() 702 | else 703 | if sum == 0 or cC == -1 then 704 | sum = 0 705 | matcher:reset() 706 | else 707 | matcher:nextStr() 708 | end 709 | end 710 | end, skip 711 | end 712 | 713 | matcher.functions[1] = function(_) 714 | matcher:fullResetOnNextStr() 715 | matcher.seqStart = matcher.str 716 | matcher:nextFunc() 717 | if (matcher.str > matcher.startStr and matcher.fromStart) or matcher.str >= matcher.stringLen then 718 | matcher.stop = true 719 | matcher.seqStart = nil 720 | end 721 | end 722 | 723 | local lastFunc 724 | local ignore = false 725 | local skip = nil 726 | local it = (function() 727 | local gen = utf8gensub(regex) 728 | return function() 729 | return gen(skip) 730 | end 731 | end)() 732 | local cs = {} 733 | for c, bs, be in it do 734 | skip = nil 735 | if plain then 736 | table.insert(matcher.functions, simple(classMatchGenerator(c, plain))) 737 | else 738 | if ignore then 739 | if find('123456789', c, 1, true) then 740 | if lastFunc then 741 | table.insert(matcher.functions, simple(lastFunc)) 742 | lastFunc = nil 743 | end 744 | table.insert(matcher.functions, capture(tonumber(c))) 745 | elseif c == 'b' then 746 | if lastFunc then 747 | table.insert(matcher.functions, simple(lastFunc)) 748 | lastFunc = nil 749 | end 750 | local b 751 | b, skip = balancer(sub(regex, be + 1, be + 9)) 752 | table.insert(matcher.functions, b) 753 | else 754 | lastFunc = classMatchGenerator('%' .. c) 755 | end 756 | ignore = false 757 | else 758 | if c == '*' then 759 | if lastFunc then 760 | table.insert(matcher.functions, star(lastFunc)) 761 | lastFunc = nil 762 | else 763 | error('invalid regex after ' .. sub(regex, 1, bs)) 764 | end 765 | elseif c == '+' then 766 | if lastFunc then 767 | table.insert(matcher.functions, simple(lastFunc)) 768 | table.insert(matcher.functions, star(lastFunc)) 769 | lastFunc = nil 770 | else 771 | error('invalid regex after ' .. sub(regex, 1, bs)) 772 | end 773 | elseif c == '-' then 774 | if lastFunc then 775 | table.insert(matcher.functions, minus(lastFunc)) 776 | lastFunc = nil 777 | else 778 | error('invalid regex after ' .. sub(regex, 1, bs)) 779 | end 780 | elseif c == '?' then 781 | if lastFunc then 782 | table.insert(matcher.functions, question(lastFunc)) 783 | lastFunc = nil 784 | else 785 | error('invalid regex after ' .. sub(regex, 1, bs)) 786 | end 787 | elseif c == '^' then 788 | if bs == 1 then 789 | matcher.fromStart = true 790 | else 791 | error('invalid regex after ' .. sub(regex, 1, bs)) 792 | end 793 | elseif c == '$' then 794 | if be == len(regex) then 795 | matcher.toEnd = true 796 | else 797 | error('invalid regex after ' .. sub(regex, 1, bs)) 798 | end 799 | elseif c == '[' then 800 | if lastFunc then 801 | table.insert(matcher.functions, simple(lastFunc)) 802 | end 803 | lastFunc, skip = classMatchGenerator(sub(regex, be + 1)) 804 | elseif c == '(' then 805 | if lastFunc then 806 | table.insert(matcher.functions, simple(lastFunc)) 807 | lastFunc = nil 808 | end 809 | table.insert(matcher.captures, {}) 810 | table.insert(cs, #matcher.captures) 811 | table.insert(matcher.functions, captureStart(cs[#cs])) 812 | if sub(regex, be + 1, be + 1) == ')' then matcher.captures[#matcher.captures].empty = true end 813 | elseif c == ')' then 814 | if lastFunc then 815 | table.insert(matcher.functions, simple(lastFunc)) 816 | lastFunc = nil 817 | end 818 | local cap = table.remove(cs) 819 | if not cap then 820 | error('invalid capture: "(" missing') 821 | end 822 | table.insert(matcher.functions, captureStop(cap)) 823 | elseif c == '.' then 824 | if lastFunc then 825 | table.insert(matcher.functions, simple(lastFunc)) 826 | end 827 | lastFunc = function(cC) return cC ~= -1 end 828 | elseif c == '%' then 829 | ignore = true 830 | else 831 | if lastFunc then 832 | table.insert(matcher.functions, simple(lastFunc)) 833 | end 834 | lastFunc = classMatchGenerator(c) 835 | end 836 | end 837 | end 838 | end 839 | if #cs > 0 then 840 | error('invalid capture: ")" missing') 841 | end 842 | if lastFunc then 843 | table.insert(matcher.functions, simple(lastFunc)) 844 | end 845 | 846 | table.insert(matcher.functions, function() 847 | if matcher.toEnd and matcher.str ~= matcher.stringLen then 848 | matcher:reset() 849 | else 850 | matcher.stop = true 851 | end 852 | end) 853 | 854 | matcher.nextFunc = function(self) 855 | self.func = self.func + 1 856 | end 857 | matcher.nextStr = function(self) 858 | self.str = self.str + 1 859 | end 860 | matcher.strReset = function(self) 861 | local oldReset = self.reset 862 | local str = self.str 863 | self.reset = function(s) 864 | s.str = str 865 | s.reset = oldReset 866 | end 867 | end 868 | matcher.fullResetOnNextFunc = function(self) 869 | local oldReset = self.reset 870 | local func = self.func +1 871 | local str = self.str 872 | self.reset = function(s) 873 | s.func = func 874 | s.str = str 875 | s.reset = oldReset 876 | end 877 | end 878 | matcher.fullResetOnNextStr = function(self) 879 | local oldReset = self.reset 880 | local str = self.str + 1 881 | local func = self.func 882 | self.reset = function(s) 883 | s.func = func 884 | s.str = str 885 | s.reset = oldReset 886 | end 887 | end 888 | 889 | matcher.process = function(self, str, start) 890 | 891 | self.func = 1 892 | start = start or 1 893 | self.startStr = (start >= 0) and start or utf8len(str) + start + 1 894 | self.seqStart = self.startStr 895 | self.str = self.startStr 896 | self.stringLen = utf8len(str) + 1 897 | self.string = str 898 | self.stop = false 899 | 900 | self.reset = function(s) 901 | s.func = 1 902 | end 903 | 904 | -- local lastPos = self.str 905 | -- local lastByte 906 | local ch 907 | while not self.stop do 908 | if self.str < self.stringLen then 909 | --[[ if lastPos < self.str then 910 | print('last byte', lastByte) 911 | ch, lastByte = utf8subWithBytes(str, 1, self.str - lastPos - 1, lastByte) 912 | ch, lastByte = utf8subWithBytes(str, 1, 1, lastByte) 913 | lastByte = lastByte - 1 914 | else 915 | ch, lastByte = utf8subWithBytes(str, self.str, self.str) 916 | end 917 | lastPos = self.str ]] 918 | ch = utf8sub(str, self.str,self.str) 919 | --print('char', ch, utf8unicode(ch)) 920 | self.functions[self.func](utf8unicode(ch)) 921 | else 922 | self.functions[self.func](-1) 923 | end 924 | end 925 | 926 | if self.seqStart then 927 | local captures = {} 928 | for _,pair in pairs(self.captures) do 929 | if pair.empty then 930 | table.insert(captures, pair[1]) 931 | else 932 | table.insert(captures, utf8sub(str, pair[1], pair[2])) 933 | end 934 | end 935 | return self.seqStart, self.str - 1, unpack(captures) 936 | end 937 | end 938 | 939 | return matcher 940 | end 941 | 942 | -- string.find 943 | local function utf8find(str, regex, init, plain) 944 | local matcher = cache[regex] or matcherGenerator(regex, plain) 945 | return matcher:process(str, init) 946 | end 947 | 948 | -- string.match 949 | local function utf8match(str, regex, init) 950 | init = init or 1 951 | local found = {utf8find(str, regex, init)} 952 | if found[1] then 953 | if found[3] then 954 | return unpack(found, 3) 955 | end 956 | return utf8sub(str, found[1], found[2]) 957 | end 958 | end 959 | 960 | -- string.gmatch 961 | local function utf8gmatch(str, regex, all) 962 | regex = (utf8sub(regex,1,1) ~= '^') and regex or '%' .. regex 963 | local lastChar = 1 964 | return function() 965 | local found = {utf8find(str, regex, lastChar)} 966 | if found[1] then 967 | lastChar = found[2] + 1 968 | if found[all and 1 or 3] then 969 | return unpack(found, all and 1 or 3) 970 | end 971 | return utf8sub(str, found[1], found[2]) 972 | end 973 | end 974 | end 975 | 976 | local function replace(repl, args) 977 | local ret = '' 978 | if type(repl) == 'string' then 979 | local ignore = false 980 | local num 981 | for c in utf8gensub(repl) do 982 | if not ignore then 983 | if c == '%' then 984 | ignore = true 985 | else 986 | ret = ret .. c 987 | end 988 | else 989 | num = tonumber(c) 990 | if num then 991 | ret = ret .. args[num] 992 | else 993 | ret = ret .. c 994 | end 995 | ignore = false 996 | end 997 | end 998 | elseif type(repl) == 'table' then 999 | ret = repl[args[1] or args[0]] or '' 1000 | elseif type(repl) == 'function' then 1001 | if #args > 0 then 1002 | ret = repl(unpack(args, 1)) or '' 1003 | else 1004 | ret = repl(args[0]) or '' 1005 | end 1006 | end 1007 | return ret 1008 | end 1009 | -- string.gsub 1010 | local function utf8gsub(str, regex, repl, limit) 1011 | limit = limit or -1 1012 | local ret = '' 1013 | local prevEnd = 1 1014 | local it = utf8gmatch(str, regex, true) 1015 | local found = {it()} 1016 | local n = 0 1017 | while #found > 0 and limit ~= n do 1018 | local args = {[0] = utf8sub(str, found[1], found[2]), unpack(found, 3)} 1019 | ret = ret .. utf8sub(str, prevEnd, found[1] - 1) 1020 | .. replace(repl, args) 1021 | prevEnd = found[2] + 1 1022 | n = n + 1 1023 | found = {it()} 1024 | end 1025 | return ret .. utf8sub(str, prevEnd), n 1026 | end 1027 | 1028 | local utf8 = {} 1029 | utf8.len = utf8len 1030 | utf8.sub = utf8sub 1031 | utf8.reverse = utf8reverse 1032 | utf8.char = utf8char 1033 | utf8.unicode = utf8unicode 1034 | utf8.gensub = utf8gensub 1035 | utf8.byte = utf8unicode 1036 | utf8.find = utf8find 1037 | utf8.match = utf8match 1038 | utf8.gmatch = utf8gmatch 1039 | utf8.gsub = utf8gsub 1040 | utf8.dump = dump 1041 | utf8.format = format 1042 | utf8.lower = lower 1043 | utf8.upper = upper 1044 | utf8.rep = rep 1045 | return utf8 1046 | -------------------------------------------------------------------------------- /libcurl.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qwe7989199/RubyTools/b71135f074e27b3831ee986a9cec918535a2b063/libcurl.dll --------------------------------------------------------------------------------