"use strict"; var cp = require("child_process"); var fs = require("fs"); var path = require("path"); var utils = require("./utils"); var zlib = require("zlib"); function classical(count) { var prevName; var thisName; var self = this; if(this.options.rotate === count) delete this.rotatedName; var callback = function(err) { if(err) return self.emit("error", err); self.open(); if(self.options.compress) self.compress(thisName); else { self.emit("rotated", self.rotatedName); self.interval(); } }; try { prevName = count === 1 ? this.name : this.generator(count - 1); thisName = this.generator(count); } catch(e) { return callback(e); } var doIt = function(done) { fs.rename(prevName, thisName, function(err) { if(err) { if(err.code !== "ENOENT") return callback(err); return utils.makePath(thisName, function(err) { if(err) return callback(err); fs.rename(prevName, thisName, function(err) { if(err) return callback(err); process.nextTick(done); }); }); } process.nextTick(done); }); }; fs.stat(prevName, function(err) { if(! err) { if(! self.rotatedName) self.rotatedName = thisName; if(count !== 1) return doIt(self.classical.bind(self, count - 1)); if(self.options.compress) return self.findName({}, true, function(err, name) { if(err) return callback(err); thisName = name; doIt(callback); }); return doIt(callback); } if(err.code !== "ENOENT") return callback(err); self.classical(count - 1); }); } function compress(tmp) { var self = this; this.findName({}, false, function(err, name) { if(err) return self.emit("error", err); self.touch(name, function(err) { if(err) return self.emit("error", err); var done = function(err) { if(err) return self.emit("error", err); fs.unlink(tmp, function(err) { if(err) self.emit("warning", err); if(self.options.rotate) self.emit("rotated", self.rotatedName); else self.emit("rotated", name); self.interval(); }); }; if(typeof self.options.compress === "function") self.external(tmp, name, done); else self.gzip(tmp, name, done); /* if(self.options.compress == "gzip") self.gzip(tmp, name, done); else throw new Error("Not implemented yet"); */ }); }); } function external(src, dst, callback) { var att = {}; var cont; var self = this; try { cont = self.options.compress(src, dst); } catch(e) { return process.nextTick(callback.bind(null, e)); } att[dst] = 1; self.findName(att, true, function(err, name) { if(err) return callback(err); fs.open(name, "w", parseInt("777", 8), function(err, fd) { if(err) return callback(err); var unlink = function(err) { fs.unlink(name, function(err2) { if(err2) self.emit("warning", err2); callback(err); }); }; fs.write(fd, cont, function(err) { fs.close(fd, function(err2) { if(err) { if(err2) self.emit("warning", err2); return unlink(err); } if(err2) return unlink(err2); if(name.indexOf(path.sep) === -1) name = "." + path.sep + name; cp.exec(name, unlink); }); }); }); }); } function exhausted(attempts) { var err = new Error("Too many destination file attempts"); err.code = "RFS-TOO-MANY"; if(attempts) err.attempts = attempts; return err; } function findName(attempts, tmp, callback) { var count = 0; for(var i in attempts) count += attempts[i]; if(count >= 1000) return callback(this.exhausted(attempts)); var name = this.name + "." + count + ".rfs.tmp"; var self = this; if(! tmp) try { var pars = [count + 1]; if(! this.options.rotate) if(this.options.interval && ! this.options.rotationTime) pars.unshift(new Date(this.prev)); else pars.unshift(this.rotation); name = this.generator.apply(this, pars); } catch(e) { return process.nextTick(callback.bind(null, e)); } if(name in attempts) { attempts[name]++; return self.findName(attempts, tmp, callback); } fs.stat(name, function(err) { if(! err || err.code !== "ENOENT") { attempts[name] = 1; return self.findName(attempts, tmp, callback); } callback(null, name); }); } function gzip(src, dst, callback) { const inp = fs.createReadStream(src); const out = fs.createWriteStream(dst); const zip = zlib.createGzip(); [inp, out, zip].map(e => e.once("error", callback)); out.once("finish", callback); inp.pipe(zip).pipe(out); } function touch(name, callback, retry) { var self = this; fs.open(name, "a", function(err, fd) { if(err && err.code !== "ENOENT" && ! retry) return callback(err); if(! err) return fs.close(fd, callback); utils.makePath(name, function(err) { if(err) return callback(err); self.touch(name, callback, true); }); }); } module.exports = { classical: classical, compress: compress, exhausted: exhausted, external: external, findName: findName, gzip: gzip, touch: touch };