IF YOU WOULD LIKE TO GET AN ACCOUNT, please write an email to s dot adaszewski at gmail dot com. User accounts are meant only to report issues and/or generate pull requests. This is a purpose-specific Git hosting for ADARED projects. Thank you for your understanding!
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.

189 lines
4.8KB

  1. //
  2. // Copyright (C) Stanislaw Adaszewski, 2020
  3. // Contact: s.adaszewski@gmail.com
  4. // Website: https://adared.ch/wba
  5. // License: GNU Affero General Public License, Version 3
  6. //
  7. function mkdir(parent, name) {
  8. if (name in parent && (parent[name] instanceof Array))
  9. throw Error('File with the same name already exists');
  10. if (name in parent)
  11. return parent[name];
  12. const dir = {};
  13. parent[name] = dir;
  14. return dir;
  15. }
  16. function mkpath(parent, path) {
  17. if (typeof(path) === 'string')
  18. path = path.split('/');
  19. let dir = parent;
  20. for (let i = 1; i < path.length; i++) {
  21. dir = mkdir(dir, path[i]);
  22. }
  23. return dir;
  24. }
  25. function makeFile(dir, name) {
  26. if (name in dir) {
  27. if (!(dir[name] instanceof Array))
  28. throw Error('Directory with the same name already exists');
  29. return dir[name];
  30. }
  31. const f = [[], 0];
  32. dir[name] = f;
  33. return f;
  34. }
  35. function appendFile(f, sidx, seg) {
  36. f[0].push([ sidx, seg[0], seg[1] ]);
  37. //f[1] += seg[1];
  38. return f;
  39. }
  40. function unescapeName(name) {
  41. return name.replace(/(\\\\|\\[0-9]{3})/g,
  42. (_, $1) => ($1 === '\\\\' ? '\\' : String.fromCharCode(parseInt($1.substr(1), 8))));
  43. }
  44. function process(streams) {
  45. const rootDir = {};
  46. streams.map((s, sidx) => {
  47. const [ streamName, locators, segments ] = s;
  48. const streamDir = mkpath(rootDir, streamName);
  49. segments.map((seg, segidx) => {
  50. let name = seg[2].split('/');
  51. const dir = (name.length === 1 ? streamDir :
  52. mkpath(streamDir, ['.'].concat(name.slice(0, name.length - 1))));
  53. name = name[name.length - 1];
  54. appendFile(dir, name, sidx, seg);
  55. });
  56. });
  57. return rootDir;
  58. }
  59. function parse(manifestText) {
  60. const M_STREAM_NAME = 0;
  61. const M_LOCATORS = 1;
  62. const M_FILE_SEGMENTS = 2;
  63. let mode = M_STREAM_NAME;
  64. const streams = [];
  65. let locators = [];
  66. let streamName;
  67. let accum = '';
  68. let tokenStart = 0;
  69. let lastFile = null;
  70. let lastPath = null;
  71. const rootDir = {};
  72. for (let i = 0; i < manifestText.length; i++) {
  73. const c = manifestText[i];
  74. if (mode === M_STREAM_NAME) {
  75. if (c === ' ') {
  76. mode = M_LOCATORS;
  77. streamName = unescapeName(accum);
  78. accum = '';
  79. tokenStart = i + 1;
  80. } else {
  81. accum += c;
  82. }
  83. } else if (mode === M_LOCATORS) {
  84. if (c === ':') {
  85. mode = M_FILE_SEGMENTS;
  86. accum = '';
  87. i = tokenStart - 1;
  88. let pos = 0;
  89. locators = locators.map(loc => {
  90. const r = loc.concat([ pos, pos + loc[1] ]);
  91. pos += loc[1];
  92. return r;
  93. });
  94. } else if (c === ' ') {
  95. const sz = Number(accum.split('+')[1]);
  96. locators.push([accum, sz]);
  97. accum = '';
  98. tokenStart = i + 1;
  99. } else {
  100. accum += c;
  101. }
  102. } else if (mode === M_FILE_SEGMENTS) {
  103. if (c === ' ' || c === '\n') {
  104. let seg = accum.split(':');
  105. seg = [Number(seg[0]), Number(seg[1]), seg.slice(2).join(':')];
  106. const path = streamName + '/' + unescapeName(seg[2]);
  107. let f;
  108. if (path !== lastPath) {
  109. let dirName = path.split('/');
  110. const fileName = dirName[dirName.length - 1];
  111. dirName = dirName.slice(0, dirName.length - 1);
  112. const dir = mkpath(rootDir, dirName);
  113. f = makeFile(dir, fileName);
  114. lastPath = path;
  115. lastFile = f;
  116. } else {
  117. f = lastFile;
  118. }
  119. appendFile(f, streams.length, seg);
  120. accum = '';
  121. tokenStart = i + 1;
  122. if (c === '\n') {
  123. streams.push([ streamName, locators ]);
  124. locators = [];
  125. mode = M_STREAM_NAME;
  126. }
  127. } else {
  128. accum += c;
  129. }
  130. }
  131. }
  132. return { rootDir, streams };
  133. }
  134. function findDir(parent, path) {
  135. if (typeof(path) === 'string')
  136. path = path.split('/');
  137. if (path[0] !== '.')
  138. throw Error('Path must start with a dot (.)');
  139. let dir = parent;
  140. for (let i = 1; i < path.length; i++) {
  141. if (!(path[i] in dir))
  142. throw Error('Directory not found');
  143. dir = dir[path[i]];
  144. }
  145. return dir;
  146. }
  147. class WBManifestReader {
  148. constructor(manifestText) {
  149. const {rootDir, streams} = parse(manifestText);
  150. this.rootDir = rootDir;
  151. this.streams = streams;
  152. //this.rootDir = process(this.streams);
  153. }
  154. listDirectory(path) {
  155. let dir = findDir(this.rootDir, path);
  156. let keys = Object.keys(dir);
  157. keys.sort();
  158. let subdirs = keys.filter(k => !(dir[k] instanceof Array));
  159. let files = keys.filter(k => (dir[k] instanceof Array));
  160. let res = subdirs.map(k => [ 'd', k, null ]);
  161. res = res.concat(files.map(k => [ 'f', k, dir[k][1] ]));
  162. return res;
  163. }
  164. }
  165. export default WBManifestReader;