// // Copyright (C) Stanislaw Adaszewski, 2020 // Contact: s.adaszewski@gmail.com // Website: https://adared.ch/wba // License: GNU Affero General Public License, Version 3 // function mkdir(parent, name) { if (name in parent && (parent[name] instanceof Array)) throw Error('File with the same name already exists'); if (name in parent) return parent[name]; const dir = {}; parent[name] = dir; return dir; } function mkpath(parent, path) { if (typeof(path) === 'string') path = path.split('/'); let dir = parent; for (let i = 1; i < path.length; i++) { dir = mkdir(dir, path[i]); } return dir; } function makeFile(dir, name) { if (name in dir) { if (!(dir[name] instanceof Array)) throw Error('Directory with the same name already exists'); return dir[name]; } const f = [[], 0]; dir[name] = f; return f; } function appendFile(f, sidx, seg) { f[0].push([ sidx, seg[0], seg[1] ]); //f[1] += seg[1]; return f; } function unescapeName(name) { return name.replace(/(\\\\|\\[0-9]{3})/g, (_, $1) => ($1 === '\\\\' ? '\\' : String.fromCharCode(parseInt($1.substr(1), 8)))); } function process(streams) { const rootDir = {}; streams.map((s, sidx) => { const [ streamName, locators, segments ] = s; const streamDir = mkpath(rootDir, streamName); segments.map((seg, segidx) => { let name = seg[2].split('/'); const dir = (name.length === 1 ? streamDir : mkpath(streamDir, ['.'].concat(name.slice(0, name.length - 1)))); name = name[name.length - 1]; appendFile(dir, name, sidx, seg); }); }); return rootDir; } function parse(manifestText) { const M_STREAM_NAME = 0; const M_LOCATORS = 1; const M_FILE_SEGMENTS = 2; let mode = M_STREAM_NAME; const streams = []; let locators = []; let streamName; let accum = ''; let tokenStart = 0; let lastFile = null; let lastPath = null; const rootDir = {}; for (let i = 0; i < manifestText.length; i++) { const c = manifestText[i]; if (mode === M_STREAM_NAME) { if (c === ' ') { mode = M_LOCATORS; streamName = unescapeName(accum); accum = ''; tokenStart = i + 1; } else { accum += c; } } else if (mode === M_LOCATORS) { if (c === ':') { mode = M_FILE_SEGMENTS; accum = ''; i = tokenStart - 1; let pos = 0; locators = locators.map(loc => { const r = loc.concat([ pos, pos + loc[1] ]); pos += loc[1]; return r; }); } else if (c === ' ') { const sz = Number(accum.split('+')[1]); locators.push([accum, sz]); accum = ''; tokenStart = i + 1; } else { accum += c; } } else if (mode === M_FILE_SEGMENTS) { if (c === ' ' || c === '\n') { let seg = accum.split(':'); seg = [Number(seg[0]), Number(seg[1]), seg.slice(2).join(':')]; const path = streamName + '/' + unescapeName(seg[2]); let f; if (path !== lastPath) { let dirName = path.split('/'); const fileName = dirName[dirName.length - 1]; dirName = dirName.slice(0, dirName.length - 1); const dir = mkpath(rootDir, dirName); f = makeFile(dir, fileName); lastPath = path; lastFile = f; } else { f = lastFile; } appendFile(f, streams.length, seg); accum = ''; tokenStart = i + 1; if (c === '\n') { streams.push([ streamName, locators ]); locators = []; mode = M_STREAM_NAME; } } else { accum += c; } } } return { rootDir, streams }; } function findDir(parent, path) { if (typeof(path) === 'string') path = path.split('/'); if (path[0] !== '.') throw Error('Path must start with a dot (.)'); let dir = parent; for (let i = 1; i < path.length; i++) { if (!(path[i] in dir)) throw Error('Directory not found'); dir = dir[path[i]]; } return dir; } class WBManifestReader { constructor(manifestText) { const {rootDir, streams} = parse(manifestText); this.rootDir = rootDir; this.streams = streams; //this.rootDir = process(this.streams); } listDirectory(path) { let dir = findDir(this.rootDir, path); let keys = Object.keys(dir); keys.sort(); let subdirs = keys.filter(k => !(dir[k] instanceof Array)); let files = keys.filter(k => (dir[k] instanceof Array)); let res = subdirs.map(k => [ 'd', k, null ]); res = res.concat(files.map(k => [ 'f', k, dir[k][1] ])); return res; } } export default WBManifestReader;