A collection of various things from the Asterisk server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

parse_manifest.lua 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. local debugls = function(l) for k, v in pairs(l) do print("dbg: " .. tostring(k) .. " => " .. tostring(v)) end end
  2. -- Not portable
  3. local separate_path = function(path)
  4. for i = #path,1,-1 do
  5. if string.sub(path, i, i) == "/" then
  6. return { dir = string.sub(path, 1, i), file = string.sub(path, i + 1, #path) }
  7. end
  8. end
  9. return { file = path }
  10. end
  11. -- Not portable
  12. local to_dir_name = function(s)
  13. if string.sub(s, #s, #s) ~= "/" then
  14. return s .. "/"
  15. else
  16. return s
  17. end
  18. end
  19. -- Not portable
  20. local abs_filename = function(cwd, path)
  21. if string.sub(path, 1, 1) == "/" then
  22. return path
  23. else
  24. if string.sub(cwd, #cwd, #cwd) ~= "/" then
  25. return to_dir_name(cwd) .. path
  26. else
  27. return to_dir_name(cwd) .. path
  28. end
  29. end
  30. end
  31. -- Not portable
  32. local normalize_abs_path = function(path, isdir)
  33. local names = {}
  34. for nm in string.gmatch(path, "[^/]+") do
  35. names[#names + 1] = nm
  36. end
  37. -- Normalize away `.`
  38. for i = #names,1,-1 do
  39. if names[i] == "." or names[i] == "" then
  40. table.remove(names, i)
  41. end
  42. end
  43. -- Normalize away `..`
  44. while names[1] == ".." do table.remove(names, 1) end
  45. local c = true
  46. while c do
  47. c = false
  48. for i = 1,(#names - 1) do
  49. if names[i + 1] == ".." then
  50. table.remove(names, i)
  51. table.remove(names, i)
  52. c = true
  53. break
  54. end
  55. end
  56. end
  57. if isdir then
  58. return "/" .. table.concat(names, "/") .. "/"
  59. else
  60. return "/" .. table.concat(names, "/")
  61. end
  62. end
  63. local path_size = function(path)
  64. local i = 0
  65. for _ in string.gmatch(path, "[^/]+") do
  66. i = i + 1
  67. end
  68. return i
  69. end
  70. -- Not portable
  71. local is_dir = function(filename)
  72. local c = false
  73. local p = io.popen("[ -d '" .. filename .. "' ] && echo ''")
  74. for l in p:lines() do c = true end
  75. p:close()
  76. return c
  77. end
  78. -- Not portable
  79. local file_exists = function(filename)
  80. local c = false
  81. local p = io.popen("[ -e '" .. filename .. "' ] && echo ''")
  82. for l in p:lines() do c = true end
  83. p:close()
  84. return c
  85. end
  86. -- Not portable
  87. local ls_dir = function(dirpath)
  88. local t = {}
  89. local p = io.popen("ls -A '" .. dirpath .. "'")
  90. for l in p:lines() do t[#t + 1] = l end
  91. p:close()
  92. return t
  93. end
  94. local read_manifest
  95. read_manifest = function(filename, file_table, errout)
  96. local parse_line = function(line, cwd)
  97. local weight_specifier
  98. local filepath
  99. for i = 1,#line do
  100. if string.sub(line, i, i) == " " then
  101. weight_specifier = string.sub(line, 1, i - 1)
  102. filepath = string.sub(line, i + 1, #line)
  103. break
  104. end
  105. end
  106. if weight_specifier == nil and filepath == nil then
  107. return nil
  108. end
  109. -- True if a category is to be weighted as a unit, thereby reducing the effective
  110. -- weight of each of its entries. This flag is meant to be employed for other manifests.
  111. local weight_whole_category = false
  112. -- Indicates that the entry is a directory whose applicable files should be read in.
  113. local directory = false
  114. -- This indicates that the entry is a manifest to be read in.
  115. local manifest = false
  116. -- This is the level to recurse into directories or the `here` case.
  117. -- `nil` indicates unbounded recursion.
  118. local recursion_level = 0
  119. -- When `weight_whole_category` is on, this specifies how deeply the categorical
  120. -- weighting will apply. Again, `nil` is unbounded.
  121. local recursive_weighting_level = 0
  122. local numstr = ""
  123. local symbol_actions =
  124. {
  125. ["#"] = function() weight_whole_category = true end,
  126. ["?"] = function() manifest = true end,
  127. ["/"] = function() if recursion_level ~= nil then recursion_level = recursion_level + 1 end end,
  128. ["*"] = function() recursion_level = nil end,
  129. ["$"] = function() if recursive_weighting_level ~= nil then recursive_weighting_level = recursive_weighting_level + 1 end end,
  130. ["%"] = function() recursive_weighting_level = nil end,
  131. ["0"] = true,
  132. ["1"] = true,
  133. ["2"] = true,
  134. ["3"] = true,
  135. ["4"] = true,
  136. ["5"] = true,
  137. ["6"] = true,
  138. ["7"] = true,
  139. ["8"] = true,
  140. ["9"] = true,
  141. ["-"] = true,
  142. ["."] = true,
  143. ["e"] = true,
  144. }
  145. for i = 1,#weight_specifier do
  146. local sym = string.sub(weight_specifier, i, i)
  147. local action = symbol_actions[sym]
  148. if action == true then
  149. numstr = numstr .. sym
  150. elseif action == nil then
  151. else
  152. action()
  153. end
  154. end
  155. local num = tonumber(numstr)
  156. if num == nil then num = 1 end
  157. return
  158. {
  159. weight = num,
  160. manifest = manifest,
  161. catweight = weight_whole_category,
  162. directory = directory,
  163. rclevel = recursion_level,
  164. rcwlevel = recursive_weighting_level,
  165. file = normalize_abs_path(abs_filename(cwd, filepath), false)
  166. }
  167. end
  168. local is_iv_name = function(s) return string.sub(s, -4, -1) == ".ivz" or string.sub(s, -3, -1) == ".iv" end
  169. local descend_dir
  170. descend_dir = function(dirpath, file_table, total_weight, catweight, rclevel, rcwlevel, source)
  171. local ftab = ls_dir(dirpath)
  172. local nrcl
  173. if rclevel == nil then nrcl = nil else nrcl = rclevel - 1 end
  174. if catweight then
  175. if rcwlevel == 0 then
  176. local temp_table = {}
  177. for _,en in pairs(ftab) do
  178. local iv = normalize_abs_path(abs_filename(dirpath, en), false)
  179. if is_dir(iv) and (rclevel == nil or rclevel > 0) then
  180. descend_dir(to_dir_name(iv), temp_table, 1, false, nrcl, 0, source)
  181. elseif is_iv_name(iv) then
  182. temp_table[#temp_table + 1] =
  183. {
  184. file = iv,
  185. -- Will be overwritten by loop below anyway
  186. }
  187. end
  188. end
  189. for _,ent in pairs(temp_table) do
  190. file_table[#file_table + 1] =
  191. {
  192. file = ent.file,
  193. weight = total_weight / #temp_table,
  194. source = source
  195. }
  196. end
  197. else
  198. for _,en in pairs(ftab) do
  199. local iv = normalize_abs_path(abs_filename(dirpath, en))
  200. if is_dir(iv) and (rclevel == nil or rclevel > 0) then
  201. if rcwlevel == nil then
  202. descend_dir(to_dir_name(iv), file_table, total_weight / #ftab, true, nrcl, nil, source)
  203. else
  204. descend_dir(to_dir_name(iv), file_table, total_weight / #ftab, true, nrcl, rcwlevel - 1, source)
  205. end
  206. elseif is_iv_name(iv) then
  207. file_table[#file_table + 1] =
  208. {
  209. file = iv,
  210. weight = total_weight / #ftab,
  211. source = source
  212. }
  213. end
  214. end
  215. end
  216. return total_weight
  217. else
  218. local twc = 0
  219. for _,en in pairs(ftab) do
  220. local iv = normalize_abs_path(abs_filename(dirpath, en))
  221. if is_dir(iv) and (rclevel == nil or rclevel > 0) then
  222. descend_dir(to_dir_name(iv), file_table, total_weight, false, nrcl, 0, source)
  223. elseif is_iv_name(iv) then
  224. twc = twc + total_weight
  225. file_table[#file_table + 1] =
  226. {
  227. file = iv,
  228. weight = total_weight,
  229. source = source
  230. }
  231. end
  232. end
  233. return twc
  234. end
  235. end
  236. if filename == nil then return nil end
  237. local mf = io.open(filename, "r")
  238. if not mf then errout("Could not open manifest " .. filename) return nil end
  239. local total_weight = 0
  240. for line in mf:lines() do
  241. local entry = parse_line(line, separate_path(filename).dir)
  242. if entry == nil then goto endloop end
  243. if entry.manifest then
  244. if not file_exists(entry.file) or is_dir(entry.file) then
  245. errout("warning: file " .. entry.file .. " is not a normal file")
  246. end
  247. local temp_table = {}
  248. local tw = read_manifest(entry.file, temp_table, errout)
  249. if entry.catweight then
  250. total_weight = total_weight + entry.weight
  251. for _,ent in pairs(temp_table) do
  252. file_table[#file_table + 1] =
  253. {
  254. file = ent.file,
  255. weight = entry.weight * ent.weight / tw,
  256. source = ent.source,
  257. }
  258. end
  259. else
  260. for _,ent in pairs(temp_table) do
  261. total_weight = total_weight + entry.weight * ent.weight
  262. file_table[#file_table + 1] =
  263. {
  264. file = ent.file,
  265. weight = entry.weight * ent.weight,
  266. source = ent.source,
  267. }
  268. end
  269. end
  270. else
  271. if not file_exists(entry.file) then
  272. errout("warning: file " .. entry.file .. " does not exist")
  273. end
  274. if is_dir(entry.file) then
  275. total_weight = total_weight + descend_dir(to_dir_name(entry.file), file_table, entry.weight, entry.catweight, entry.rclevel, entry.rcwlevel, { file = entry.file, manifest = normalize_abs_path(filename) })
  276. else
  277. total_weight = total_weight + entry.weight
  278. file_table[#file_table + 1] =
  279. {
  280. file = entry.file,
  281. weight = entry.weight,
  282. source = { file = entry.file, manifest = normalize_abs_path(filename) }
  283. }
  284. end
  285. end
  286. ::endloop::
  287. end
  288. mf:close()
  289. return total_weight
  290. end
  291. local assemble_weights = function(entries, errout)
  292. local ivs = {}
  293. for _, ent in pairs(entries) do
  294. if ivs[ent.file] ~= nil then
  295. local sza = path_size(ivs[ent.file].source.file)
  296. local szb = path_size(ent.source.file)
  297. if sza < szb then
  298. ivs[ent.file] = { weight = ent.weight, source = ent.source }
  299. elseif sza == szb then
  300. local namea = ivs[ent.file].source.manifest
  301. local nameb = ent.source.manifest
  302. if namea == nameb then
  303. errout("warning: two conflicting weights for " .. ent.file .. " in " .. namea)
  304. else
  305. errout("warning: two conflicting weights for " .. ent.file .. " in " .. namea .. " and " .. nameb)
  306. end
  307. end
  308. else
  309. ivs[ent.file] = { weight = ent.weight, source = ent.source }
  310. end
  311. end
  312. return ivs
  313. end
  314. local function usage()
  315. print("usage: <this program> <input manifest> <output table>")
  316. end
  317. if arg[1] == nil or arg[2] == nil then
  318. usage()
  319. return
  320. end
  321. local taboutf = io.open(arg[2], "w")
  322. if taboutf == nil then
  323. print("Could not open output file")
  324. return
  325. end
  326. local tab = {}
  327. read_manifest(arg[1], tab, function(s) print(s) end)
  328. local weightmap = assemble_weights(tab, function(s) print(s) end)
  329. local cumweights = {}
  330. local cum = 0.0
  331. for file, wt in pairs(weightmap) do
  332. cum = cum + wt.weight
  333. table.insert(cumweights, { cum = cum, file = file, wt = wt })
  334. end
  335. for idx, ent in ipairs(cumweights) do
  336. taboutf:write(tostring(ent.cum) .. " : " .. ent.file .. "\n")
  337. end
  338. taboutf:close()